Skip to main content
โšก Calmops

Cryptography in Go

Cryptography in Go

Introduction

Cryptography is essential for protecting sensitive data. Go’s crypto package provides robust implementations of modern cryptographic algorithms. This guide covers encryption, hashing, digital signatures, and secure key management.

Core Concepts

Types of Cryptography

  1. Symmetric Encryption: Same key for encryption and decryption (AES)
  2. Asymmetric Encryption: Different keys for encryption and decryption (RSA)
  3. Hashing: One-way function producing fixed-size output (SHA-256)
  4. Digital Signatures: Prove authenticity and non-repudiation

Good: Hashing

SHA-256 Hashing

package main

import (
	"crypto/sha256"
	"fmt"
	"io"
)

// โœ… GOOD: Hash sensitive data
func HashPassword(password string) string {
	hash := sha256.Sum256([]byte(password))
	return fmt.Sprintf("%x", hash)
}

// โœ… GOOD: Hash file
func HashFile(filename string) (string, error) {
	file, err := os.Open(filename)
	if err != nil {
		return "", err
	}
	defer file.Close()
	
	hash := sha256.New()
	if _, err := io.Copy(hash, file); err != nil {
		return "", err
	}
	
	return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

func main() {
	password := "mypassword"
	hashed := HashPassword(password)
	fmt.Printf("Hash: %s\n", hashed)
}

Good: Symmetric Encryption

AES Encryption

package main

import (
	"crypto/aes"
	"crypto/cipher"
	"crypto/rand"
	"fmt"
	"io"
)

// โœ… GOOD: AES-256 encryption
func EncryptData(plaintext string, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	
	nonce := make([]byte, gcm.NonceSize())
	if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
		return "", err
	}
	
	ciphertext := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
	return fmt.Sprintf("%x", ciphertext), nil
}

// โœ… GOOD: AES decryption
func DecryptData(ciphertext string, key []byte) (string, error) {
	block, err := aes.NewCipher(key)
	if err != nil {
		return "", err
	}
	
	gcm, err := cipher.NewGCM(block)
	if err != nil {
		return "", err
	}
	
	data := make([]byte, hex.DecodedLen(len(ciphertext)))
	if _, err := hex.Decode(data, []byte(ciphertext)); err != nil {
		return "", err
	}
	
	nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
	plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
	if err != nil {
		return "", err
	}
	
	return string(plaintext), nil
}

func main() {
	key := make([]byte, 32) // 256-bit key
	rand.Read(key)
	
	plaintext := "sensitive data"
	encrypted, _ := EncryptData(plaintext, key)
	decrypted, _ := DecryptData(encrypted, key)
	
	fmt.Printf("Original: %s\n", plaintext)
	fmt.Printf("Decrypted: %s\n", decrypted)
}

Good: Asymmetric Encryption

RSA Encryption

package main

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

// โœ… GOOD: Generate RSA key pair
func GenerateRSAKeyPair() (*rsa.PrivateKey, error) {
	return rsa.GenerateKey(rand.Reader, 2048)
}

// โœ… GOOD: RSA encryption
func EncryptWithRSA(plaintext string, publicKey *rsa.PublicKey) (string, error) {
	ciphertext, err := rsa.EncryptOAEP(
		sha256.New(),
		rand.Reader,
		publicKey,
		[]byte(plaintext),
		nil,
	)
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%x", ciphertext), nil
}

// โœ… GOOD: RSA decryption
func DecryptWithRSA(ciphertext string, privateKey *rsa.PrivateKey) (string, error) {
	data := make([]byte, hex.DecodedLen(len(ciphertext)))
	hex.Decode(data, []byte(ciphertext))
	
	plaintext, err := rsa.DecryptOAEP(
		sha256.New(),
		rand.Reader,
		privateKey,
		data,
		nil,
	)
	if err != nil {
		return "", err
	}
	return string(plaintext), nil
}

func main() {
	privateKey, _ := GenerateRSAKeyPair()
	publicKey := &privateKey.PublicKey
	
	plaintext := "secret message"
	encrypted, _ := EncryptWithRSA(plaintext, publicKey)
	decrypted, _ := DecryptWithRSA(encrypted, privateKey)
	
	fmt.Printf("Original: %s\n", plaintext)
	fmt.Printf("Decrypted: %s\n", decrypted)
}

Good: Digital Signatures

RSA Signatures

package main

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

// โœ… GOOD: Sign data with RSA
func SignData(data string, privateKey *rsa.PrivateKey) (string, error) {
	hash := sha256.Sum256([]byte(data))
	
	signature, err := rsa.SignPKCS1v15(
		rand.Reader,
		privateKey,
		crypto.SHA256,
		hash[:],
	)
	if err != nil {
		return "", err
	}
	
	return fmt.Sprintf("%x", signature), nil
}

// โœ… GOOD: Verify signature
func VerifySignature(data, signature string, publicKey *rsa.PublicKey) error {
	hash := sha256.Sum256([]byte(data))
	
	sig := make([]byte, hex.DecodedLen(len(signature)))
	hex.Decode(sig, []byte(signature))
	
	return rsa.VerifyPKCS1v15(
		publicKey,
		crypto.SHA256,
		hash[:],
		sig,
	)
}

func main() {
	privateKey, _ := GenerateRSAKeyPair()
	publicKey := &privateKey.PublicKey
	
	data := "important message"
	signature, _ := SignData(data, privateKey)
	
	if err := VerifySignature(data, signature, publicKey); err != nil {
		fmt.Println("Signature verification failed")
	} else {
		fmt.Println("Signature verified successfully")
	}
}

Best Practices

1. Use Strong Keys

// โœ… GOOD: 256-bit key for AES
key := make([]byte, 32)
rand.Read(key)

// โœ… GOOD: 2048-bit RSA key
privateKey, _ := rsa.GenerateKey(rand.Reader, 2048)

// โŒ BAD: Weak key
key := []byte("password123")

2. Use Authenticated Encryption

// โœ… GOOD: GCM mode provides authentication
gcm, _ := cipher.NewGCM(block)
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)

// โŒ BAD: ECB mode (no authentication)
cipher.NewCBCEncrypter(block, iv)

3. Secure Key Storage

// โœ… GOOD: Use environment variables or key management services
key := os.Getenv("ENCRYPTION_KEY")

// โŒ BAD: Hardcoded keys
key := "hardcoded-secret-key"

Resources

Summary

Go’s crypto package provides secure implementations of modern cryptographic algorithms. Always use authenticated encryption, strong keys, and secure key management practices.

Comments