Introduction
Security is critical in modern applications. Go provides robust standard libraries for implementing secure authentication and encryption. In this comprehensive guide, we'll explore:
- Token Generation - Creating secure random tokens
- SHA1 Hashing - Understanding cryptographic hashing
- RSA Encryption - Asymmetric encryption for secure data transmission
- AES Encryption - Symmetric encryption for fast, secure data storage
1. Token Generation
Tokens are used for authentication, session management, and API keys. Let's generate cryptographically secure random tokens.
Basic Random Token
package main
import (
"crypto/rand"
"encoding/hex"
"fmt"
)
// GenerateToken creates a random hex token
func GenerateToken(length int) (string, error) {
bytes := make([]byte, length)
if _, err := rand.Read(bytes); err != nil {
return "", err
}
return hex.EncodeToString(bytes), nil
}
func main() {
token, err := GenerateToken(32)
if err != nil {
panic(err)
}
fmt.Println("Token:", token)
}
Key Points:
- Always use
crypto/randfor security-critical random generation - Never use
math/randfor tokens or keys - Common token sizes: 16, 32, or 64 bytes
UUID-based Token
import "github.com/google/uuid"
func GenerateUUIDToken() string {
return uuid.New().String()
}
2. SHA1 Hashing
Note: SHA1 is cryptographically broken for collision resistance. Use SHA256 or SHA512 for new applications. This example is for educational purposes.
package main
import (
"crypto/sha1"
"encoding/hex"
"fmt"
)
func HashSHA1(data string) string {
hash := sha1.New()
hash.Write([]byte(data))
return hex.EncodeToString(hash.Sum(nil))
}
func main() {
data := "Hello, Secure World!"
hashed := HashSHA1(data)
fmt.Printf("Original: %s\n", data)
fmt.Printf("SHA1: %s\n", hashed)
}
Better Alternative: SHA256
import "crypto/sha256"
func HashSHA256(data string) string {
hash := sha256.Sum256([]byte(data))
return hex.EncodeToString(hash[:])
}
Use Cases:
- File integrity verification
- Data fingerprinting
- Password hashing (with salt and iterations)
3. RSA Encryption
RSA is an asymmetric encryption algorithm using public/private key pairs.
Generate RSA Keys
package main
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
"os"
)
func GenerateRSAKeys(bits int) (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, bits)
if err != nil {
return nil, err
}
return privateKey, nil
}
func SavePrivateKey(filename string, key *rsa.PrivateKey) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
privateKeyBytes := x509.MarshalPKCS1PrivateKey(key)
privateKeyPEM := &pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: privateKeyBytes,
}
return pem.Encode(file, privateKeyPEM)
}
func SavePublicKey(filename string, key *rsa.PublicKey) error {
file, err := os.Create(filename)
if err != nil {
return err
}
defer file.Close()
publicKeyBytes, err := x509.MarshalPKIXPublicKey(key)
if err != nil {
return err
}
publicKeyPEM := &pem.Block{
Type: "PUBLIC KEY",
Bytes: publicKeyBytes,
}
return pem.Encode(file, publicKeyPEM)
}
RSA Encryption & Decryption
import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
)
func EncryptRSA(publicKey *rsa.PublicKey, message []byte) ([]byte, error) {
hash := sha256.New()
ciphertext, err := rsa.EncryptOAEP(hash, rand.Reader, publicKey, message, nil)
if err != nil {
return nil, err
}
return ciphertext, nil
}
func DecryptRSA(privateKey *rsa.PrivateKey, ciphertext []byte) ([]byte, error) {
hash := sha256.New()
plaintext, err := rsa.DecryptOAEP(hash, rand.Reader, privateKey, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
// Example usage
func main() {
privateKey, _ := GenerateRSAKeys(2048)
publicKey := &privateKey.PublicKey
message := []byte("Secret message")
encrypted, _ := EncryptRSA(publicKey, message)
decrypted, _ := DecryptRSA(privateKey, encrypted)
fmt.Printf("Decrypted: %s\n", decrypted)
}
4. AES Encryption
AES (Advanced Encryption Standard) is a symmetric encryption algorithm - fast and secure.
AES-GCM Mode (Recommended)
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
)
func EncryptAES(key []byte, plaintext []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
}
nonce := make([]byte, gcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
return nil, err
}
ciphertext := gcm.Seal(nonce, nonce, plaintext, nil)
return ciphertext, nil
}
func DecryptAES(key []byte, ciphertext []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, fmt.Errorf("ciphertext too short")
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return plaintext, nil
}
Complete AES Example
func main() {
// AES-256 requires 32-byte key
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
panic(err)
}
plaintext := []byte("Sensitive data to encrypt")
fmt.Printf("Original: %s\n", plaintext)
encrypted, err := EncryptAES(key, plaintext)
if err != nil {
panic(err)
}
fmt.Printf("Encrypted: %x\n", encrypted)
decrypted, err := DecryptAES(key, encrypted)
if err != nil {
panic(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
Key Sizes:
- AES-128: 16 bytes
- AES-192: 24 bytes
- AES-256: 32 bytes (recommended)
Best Practices
Security Checklist
-
Random Generation
- Always use
crypto/rand, nevermath/rand - Generate keys with sufficient entropy
- Always use
-
Key Management
- Store keys securely (environment variables, key vaults)
- Never hardcode keys in source code
- Rotate keys regularly
-
Algorithm Selection
- Use SHA256/SHA512 instead of SHA1
- Prefer AES-GCM for authenticated encryption
- Use RSA with OAEP padding
-
Error Handling
- Never ignore cryptographic errors
- Use constant-time comparisons for secrets
Common Pitfalls to Avoid
- Don't use ECB mode for AES
- Don't reuse nonces in AES-GCM
- Don't use small RSA key sizes (< 2048 bits)
- Don't roll your own crypto primitives
Conclusion
Go's crypto package provides production-ready cryptographic primitives:
-
Tokens: Use
crypto/randfor secure random generation - Hashing: Prefer SHA256+ for integrity and fingerprinting
- RSA: Ideal for key exchange and digital signatures
- AES-GCM: Best choice for encrypting data at rest and in transit
Remember: cryptography is complex. When in doubt, consult security experts and use established libraries. Stay secure! 🔐
Top comments (0)