πŸ” Security✍️ KhoaπŸ“… 19/04/2026β˜• 11 phΓΊt đọc

Cryptography Basics cho Developers

Cryptography khΓ΄ng phαΊ£i lΓ  magic β€” Δ‘Γ³ lΓ  toΓ‘n học. NhΖ°ng bαΊ‘n khΓ΄ng cαΊ§n lΓ  mathematician để dΓΉng crypto Δ‘ΓΊng cΓ‘ch. Section nΓ y giαΊ£i thΓ­ch cΓ‘c concepts cΖ‘ bαΊ£n vΓ  cΓ‘ch apply an toΓ n trong code.

πŸ” Golden rule: "Don't roll your own crypto." DΓΉng battle-tested libraries, khΓ΄ng tα»± implement algorithms.


Encoding vs Hashing vs Encryption

Ba khΓ‘i niệm nΓ y thường bα»‹ nhαΊ§m lαΊ«n. HΓ£y clear ngay tα»« Δ‘αΊ§u:

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ENCODING: Reversible, NO security                       β”‚
β”‚  Base64, URL encoding, hex                               β”‚
β”‚  Purpose: Data representation, not security              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  HASHING: One-way, NO key needed                         β”‚
β”‚  SHA-256, bcrypt, argon2                                 β”‚
β”‚  Purpose: Integrity check, password storage              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  ENCRYPTION: Reversible, KEY required                    β”‚
β”‚  AES, RSA                                                β”‚
β”‚  Purpose: Confidentiality (hide data)                    β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Encoding Example

import "encoding/base64"

// Encode (not secure!)
plaintext := "password123"
encoded := base64.StdEncoding.EncodeToString([]byte(plaintext))
// β†’ "cGFzc3dvcmQxMjM="

// Decode (anyone can do this)
decoded, _ := base64.StdEncoding.DecodeString(encoded)
// β†’ "password123"

Use case: Truyền binary data qua text protocols (email attachments, JSON).
NOT for: Hiding sensitive data!


Hashing

Hash function: Input β†’ Fixed-size output (digest/hash)
Properties:

  • Deterministic: Same input β†’ same output
  • One-way: Cannot reverse (hash β†’ original)
  • Collision-resistant: Hard to find two inputs with same hash
  • Avalanche effect: Small change in input β†’ completely different hash

Common Hash Functions

import "crypto/sha256"

text := "Hello, World!"
hash := sha256.Sum256([]byte(text))
fmt.Printf("%x\n", hash)
// β†’ dffd6021bb2bd5b0af676290809ec3a53191dd81c7f70a4b28688a362182986f

// Change one character
text2 := "Hello, world!" // lowercase 'w'
hash2 := sha256.Sum256([]byte(text2))
fmt.Printf("%x\n", hash2)
// β†’ 315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3
// Completely different!
Algorithm Output Size Status Use Case
MD5 128 bits ❌ Broken NEVER use
SHA-1 160 bits ❌ Deprecated Legacy systems only
SHA-256 256 bits βœ… Good General-purpose hashing
SHA-512 512 bits βœ… Good When need larger hash
SHA-3 Variable βœ… Good Latest standard

Password Hashing (Special Case)

Problem: SHA-256 is too FAST β†’ attackers can brute-force billions of hashes/sec.

Solution: Use password hashing functions designed to be slow:

  • bcrypt
  • scrypt
  • argon2 (winner of password hashing competition)
import "golang.org/x/crypto/bcrypt"

// Hash password (with salt automatically included)
func hashPassword(password string) (string, error) {
    hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
    // Cost = 10 means 2^10 iterations (adjustable)
    return string(hash), err
}

// Verify password
func checkPassword(password, hash string) bool {
    err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
    return err == nil
}

// Example
hashedPassword, _ := hashPassword("mypassword")
// β†’ $2a$10$N9qo8uLOickgx2ZMRZoMye.IjkkXyZvF0b0HW.5gYsVgU6TQ5VlGm

// Verify
isValid := checkPassword("mypassword", hashedPassword)  // true
isValid = checkPassword("wrongpassword", hashedPassword) // false

Key features:

  • βœ… Salt automatically included in output
  • βœ… Work factor (cost) tunable β†’ increase as hardware improves
  • βœ… Slow by design β†’ resistant to brute-force

argon2 (recommended for new systems):

import "golang.org/x/crypto/argon2"

func hashPasswordArgon2(password string) string {
    salt := make([]byte, 16)
    rand.Read(salt)

    hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
    // time=1, memory=64MB, threads=4, keyLen=32

    // Encode salt + hash for storage
    return fmt.Sprintf("%x:%x", salt, hash)
}

Symmetric Encryption

Symmetric: Same key for encrypt & decrypt.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Key    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”    Key    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Plain   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Cipher  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Plain   β”‚
β”‚  text   β”‚  Encrypt  β”‚  text   β”‚  Decrypt  β”‚  text   β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜           β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

AES-GCM = AES encryption + authentication (ensures integrity).

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

// Encrypt
func encrypt(plaintext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key) // key must be 16, 24, or 32 bytes
    if err != nil {
        return nil, err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    // Generate random nonce (number used once)
    nonce := make([]byte, gcm.NonceSize())
    if _, err := rand.Read(nonce); err != nil {
        return nil, err
    }

    // Encrypt and authenticate
    ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
    // Prepend nonce to ciphertext (nonce is public, not secret)

    return ciphertext, nil
}

// Decrypt
func decrypt(ciphertext, key []byte) ([]byte, error) {
    block, err := aes.NewCipher(key)
    if err != nil {
        return nil, err
    }

    gcm, err := cipher.NewGCM(block)
    if err != nil {
        return nil, err
    }

    nonceSize := gcm.NonceSize()
    if len(ciphertext) < nonceSize {
        return nil, errors.New("ciphertext too short")
    }

    nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
    
    plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
    if err != nil {
        return nil, err // Authentication failed or tampered
    }

    return plaintext, nil
}

// Usage
key := []byte("32-byte-long-key-for-aes-256!!!")
plaintext := []byte("Secret message")

encrypted, _ := encrypt(plaintext, key)
decrypted, _ := decrypt(encrypted, key)

fmt.Println(string(decrypted)) // "Secret message"

Key points:

  • βœ… Use GCM mode (not ECB or CBC alone)
  • βœ… Nonce must be unique for each encryption (never reuse!)
  • βœ… Key size: 16 bytes (AES-128), 24 (AES-192), or 32 (AES-256)
  • βœ… GCM provides authentication β†’ detects tampering

Common Mistakes

❌ Reusing nonces:

// πŸ”₯ NEVER do this
nonce := []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}

❌ Using ECB mode:

// πŸ”₯ ECB leaks patterns (never use for real data)

❌ No integrity check:

// πŸ”₯ Using AES-CBC without HMAC β†’ vulnerable to padding oracle

Asymmetric Encryption (Public Key Cryptography)

Asymmetric: Different keys for encrypt & decrypt.

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Public Key    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Private Key   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  Plaintext β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Cipher   β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚  Plaintext β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   (Encrypt)     β”‚  text    β”‚   (Decrypt)     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Use cases:

  • Encrypt data for someone (use their public key)
  • Establish shared secret (Diffie-Hellman)
  • Digital signatures

RSA Example

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

// Generate key pair
func generateKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, error) {
    privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
    if err != nil {
        return nil, nil, err
    }
    return privateKey, &privateKey.PublicKey, nil
}

// Encrypt with public key
func encryptRSA(plaintext []byte, publicKey *rsa.PublicKey) ([]byte, error) {
    return rsa.EncryptOAEP(sha256.New(), rand.Reader, publicKey, plaintext, nil)
}

// Decrypt with private key
func decryptRSA(ciphertext []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
    return privateKey.Decrypt(rand.Reader, ciphertext, &rsa.OAEPOptions{Hash: crypto.SHA256})
}

// Usage
privateKey, publicKey, _ := generateKeyPair()

plaintext := []byte("Secret message")
ciphertext, _ := encryptRSA(plaintext, publicKey)
decrypted, _ := decryptRSA(ciphertext, privateKey)

fmt.Println(string(decrypted)) // "Secret message"

Limitations:

  • ❌ Slow compared to symmetric encryption
  • ❌ Can only encrypt small data (max = key size - padding)

Solution: Hybrid encryption (RSA + AES):

// 1. Generate random AES key
aesKey := make([]byte, 32)
rand.Read(aesKey)

// 2. Encrypt data with AES
ciphertext, _ := encrypt(largeData, aesKey)

// 3. Encrypt AES key with RSA
encryptedKey, _ := encryptRSA(aesKey, recipientPublicKey)

// Send both encryptedKey + ciphertext

This is how TLS works!


Digital Signatures

Signatures prove:

  1. Authentication: Message came from specific sender
  2. Integrity: Message not modified
  3. Non-repudiation: Sender cannot deny sending
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Private Key   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Message  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Signature β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   (Sign)        β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   Public Key    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Message  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚ Valid?     β”‚
β”‚ + Sig    β”‚   (Verify)      β”‚ True/False β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                 β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

RSA Signature

import "crypto"

// Sign message
func signMessage(message []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
    hashed := sha256.Sum256(message)
    return rsa.SignPKCS1v15(rand.Reader, privateKey, crypto.SHA256, hashed[:])
}

// Verify signature
func verifySignature(message, signature []byte, publicKey *rsa.PublicKey) error {
    hashed := sha256.Sum256(message)
    return rsa.VerifyPKCS1v15(publicKey, crypto.SHA256, hashed[:], signature)
}

// Usage
privateKey, publicKey, _ := generateKeyPair()

message := []byte("Important contract")
signature, _ := signMessage(message, privateKey)

// Verify
err := verifySignature(message, signature, publicKey)
if err == nil {
    fmt.Println("Signature valid!")
} else {
    fmt.Println("Signature INVALID!")
}

// Tamper with message
tamperedMessage := []byte("Tampered contract")
err = verifySignature(tamperedMessage, signature, publicKey)
// β†’ Error: verification failed

HMAC (Symmetric Signature)

When both parties share a secret key:

import "crypto/hmac"

// Generate HMAC
func generateHMAC(message, key []byte) []byte {
    mac := hmac.New(sha256.New, key)
    mac.Write(message)
    return mac.Sum(nil)
}

// Verify HMAC
func verifyHMAC(message, receivedMAC, key []byte) bool {
    expectedMAC := generateHMAC(message, key)
    return hmac.Equal(receivedMAC, expectedMAC)
}

// Usage
key := []byte("shared-secret-key")
message := []byte("Important data")

mac := generateHMAC(message, key)

// Verify
isValid := verifyHMAC(message, mac, key) // true

// Tampered
tamperedMessage := []byte("Tampered data")
isValid = verifyHMAC(tamperedMessage, mac, key) // false

HMAC vs Digital Signature:

HMAC Digital Signature
Keys Symmetric (same key) Asymmetric (public/private)
Speed βœ… Fast ❌ Slower
Non-repudiation ❌ No (both have key) βœ… Yes
Use case API authentication Documents, software signing

Key Management

The hardest part of crypto is managing keys.

Don't Do This

// πŸ”₯ NEVER hardcode keys
var encryptionKey = []byte("my-secret-key-123")

// πŸ”₯ NEVER commit keys to git
// .env
ENCRYPTION_KEY=abc123xyz

Key Derivation (from password)

import "golang.org/x/crypto/scrypt"

// Derive encryption key from password
func deriveKey(password, salt []byte) ([]byte, error) {
    return scrypt.Key(password, salt, 32768, 8, 1, 32)
    // N=32768, r=8, p=1, keyLen=32
}

// Usage
password := []byte("user-password")
salt := make([]byte, 16)
rand.Read(salt)

key, _ := deriveKey(password, salt)
// Now use 'key' for AES encryption

// Store salt alongside encrypted data (salt is not secret)

Key Rotation

type EncryptedData struct {
    KeyID      int       // Which key was used
    Ciphertext []byte
    CreatedAt  time.Time
}

var keys = map[int][]byte{
    1: []byte("old-key-aaaaaaaaaaaaaaaaaaaaaaa!"),
    2: []byte("new-key-bbbbbbbbbbbbbbbbbbbbbbb!"),
}

var currentKeyID = 2

func encryptWithRotation(plaintext []byte) (*EncryptedData, error) {
    key := keys[currentKeyID]
    ciphertext, err := encrypt(plaintext, key)
    if err != nil {
        return nil, err
    }

    return &EncryptedData{
        KeyID:      currentKeyID,
        Ciphertext: ciphertext,
        CreatedAt:  time.Now(),
    }, nil
}

func decryptWithRotation(data *EncryptedData) ([]byte, error) {
    key, exists := keys[data.KeyID]
    if !exists {
        return nil, errors.New("key not found")
    }

    return decrypt(data.Ciphertext, key)
}

// Background job: Re-encrypt old data with new key
func rotateKeys() {
    oldDataList := getDataEncryptedWithKey(1)
    for _, oldData := range oldDataList {
        // Decrypt with old key
        plaintext, _ := decrypt(oldData.Ciphertext, keys[1])
        
        // Encrypt with new key
        newData, _ := encryptWithRotation(plaintext)
        
        // Update in DB
        db.Update(oldData.ID, newData)
    }
}

Use Key Management Services (Production)

AWS KMS:

import "github.com/aws/aws-sdk-go/service/kms"

func encryptWithKMS(plaintext []byte, keyID string) ([]byte, error) {
    svc := kms.New(session.Must(session.NewSession()))
    
    result, err := svc.Encrypt(&kms.EncryptInput{
        KeyId:     aws.String(keyID),
        Plaintext: plaintext,
    })
    
    return result.CiphertextBlob, err
}

func decryptWithKMS(ciphertext []byte) ([]byte, error) {
    svc := kms.New(session.Must(session.NewSession()))
    
    result, err := svc.Decrypt(&kms.DecryptInput{
        CiphertextBlob: ciphertext,
    })
    
    return result.Plaintext, err
}

TLS/SSL (Transport Layer Security)

TLS secures communication between client & server.

TLS Handshake (Simplified)

Client                                Server
  β”‚                                     β”‚
  β”‚ β‘  ClientHello                       β”‚
  β”‚   (supported ciphers, TLS version)  β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚
  β”‚                                     β”‚
  β”‚                       β‘‘ ServerHello β”‚
  β”‚        (chosen cipher, certificate) β”‚
  │◄─────────────────────────────────────
  β”‚                                     β”‚
  β”‚ β‘’ Verify certificate                β”‚
  β”‚   (signed by trusted CA?)           β”‚
  β”‚                                     β”‚
  β”‚ β‘£ Generate pre-master secret        β”‚
  β”‚   Encrypt with server's public key  β”‚
  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Ίβ”‚
  β”‚                                     β”‚
  β”‚                     β‘€ Both derive   β”‚
  β”‚                       session keys  β”‚
  β”‚                       (symmetric)   β”‚
  β”‚                                     β”‚
  β”‚ β‘₯ Encrypted communication           β”‚
  │◄───────────────────────────────────►│
  β”‚   (using AES with session key)      β”‚

Key points:

  • Uses asymmetric crypto to exchange symmetric key
  • Then uses symmetric crypto (AES) for actual data (faster)
  • Certificate proves server identity

Implementing HTTPS in Go

func main() {
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, HTTPS!")
    })

    // Generate self-signed cert for dev:
    // openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes

    log.Fatal(http.ListenAndServeTLS(":443", "cert.pem", "key.pem", nil))
}

Production: Use Let's Encrypt (free, auto-renewing certificates)


Random Number Generation

Cryptographically secure random is critical for:

  • Generating keys
  • Nonces
  • Tokens
  • Salts

❌ Wrong

import "math/rand"

// πŸ”₯ NEVER use math/rand for security
token := rand.Int() // Predictable!

βœ… Correct

import "crypto/rand"

// Generate random bytes
func generateRandomBytes(n int) ([]byte, error) {
    b := make([]byte, n)
    _, err := rand.Read(b)
    return b, err
}

// Generate random token
func generateToken() (string, error) {
    b, err := generateRandomBytes(32)
    if err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b), nil
}

// Usage
token, _ := generateToken()
// β†’ "a8f3jkd9f0aksdjf0a9sdjf0a9sdjf0as9df"

Common Pitfalls

1. Using ECB Mode

// πŸ”₯ NEVER use ECB (Electronic Codebook) mode
// Identical plaintext blocks β†’ identical ciphertext blocks
// Leaks patterns (e.g., penguin image meme)

2. Not Using Authenticated Encryption

// πŸ”₯ AES-CBC without HMAC
// β†’ Vulnerable to padding oracle attacks

// βœ… Use AES-GCM (authenticated encryption)

3. Reusing Nonces/IVs

// πŸ”₯ Same nonce for multiple encryptions
// β†’ Breaks security guarantees

// βœ… Generate new random nonce for EACH encryption

4. Weak Random Number Generator

// πŸ”₯ math/rand for crypto
// β†’ Predictable

// βœ… crypto/rand

5. Short Keys

// πŸ”₯ Short keys are brute-forceable
key := []byte("abc") // 3 bytes = 24 bits

// βœ… Use at least 128 bits (16 bytes) for symmetric
key := make([]byte, 32) // 256 bits for AES-256
rand.Read(key)

TΓ³m tαΊ―t

Operation Algorithm Use Case
Password hashing bcrypt, argon2 Storing passwords
General hashing SHA-256, SHA-3 Checksums, signatures
Symmetric encryption AES-GCM Encrypting data at rest
Asymmetric encryption RSA, ECC Key exchange, signatures
Message authentication HMAC API authentication
Digital signatures RSA, ECDSA Document signing, JWT
Key derivation PBKDF2, scrypt Password β†’ encryption key
Random generation crypto/rand Keys, nonces, tokens

Golden rules:

  1. βœ… Don't roll your own crypto
  2. βœ… Use standard libraries (Go crypto, OpenSSL, libsodium)
  3. βœ… Use authenticated encryption (AES-GCM, not AES-CBC alone)
  4. βœ… Never reuse nonces
  5. βœ… Use crypto/rand, not math/rand
  6. βœ… Rotate keys regularly
  7. βœ… Use KMS in production

BΖ°α»›c tiαΊΏp theo

  • auth/oauth2-and-oidc.md β€” How JWT signatures work
  • distributed-systems-security.md β€” mTLS certificates & PKI
  • api-and-web-security.md β€” HMAC for API authentication