Encryption is a critical aspect of software security, ensuring that sensitive data remains confidential. In this tutorial, we will explore how to implement encryption and decryption in Go using the crypto package, which provides cryptographic primitives for secure communication.
Before diving into the implementation details, ensure you have the following:
In this tutorial, we will cover:
Symmetric encryption uses the same key for both encryption and decryption. AES is a widely used symmetric encryption algorithm.
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
)
AES requires a key of either 16, 24, or 32 bytes. For this example, we'll use a 32-byte key.
func generateKey() ([]byte, error) {
key := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, key); err != nil {
return nil, err
}
return key, nil
}
func encrypt(data []byte, 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, data, nil)
return base64.URLEncoding.EncodeToString(ciphertext), nil
}
func decrypt(token string, key []byte) ([]byte, error) {
ciphertext, err := base64.URLEncoding.DecodeString(token)
if err != nil {
return nil, err
}
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
}
func main() {
key, _ := generateKey()
data := []byte("Hello, World!")
encryptedData, _ := encrypt(data, key)
fmt.Println("Encrypted:", encryptedData)
decryptedData, _ := decrypt(encryptedData, key)
fmt.Println("Decrypted:", string(decryptedData))
}
Asymmetric encryption uses a pair of keys: a public key for encryption and a private key for decryption. RSA is commonly used for secure data transmission.
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"encoding/pem"
"fmt"
)
func generateRSAKeys() (*rsa.PrivateKey, error) {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return nil, err
}
return privateKey, nil
}
func encryptWithRSA(data []byte, publicKey *rsa.PublicKey) ([]byte, error) {
encryptedData, err := rsa.EncryptPKCS1v15(rand.Reader, publicKey, data)
if err != nil {
return nil, err
}
return encryptedData, nil
}
func decryptWithRSA(encryptedData []byte, privateKey *rsa.PrivateKey) ([]byte, error) {
decryptedData, err := rsa.DecryptPKCS1v15(rand.Reader, privateKey, encryptedData)
if err != nil {
return nil, err
}
return decryptedData, nil
}
func main() {
privateKey, _ := generateRSAKeys()
publicKey := &privateKey.PublicKey
data := []byte("Hello, World!")
encryptedData, _ := encryptWithRSA(data, publicKey)
fmt.Println("Encrypted:", base64.URLEncoding.EncodeToString(encryptedData))
decryptedData, _ := decryptWithRSA(encryptedData, privateKey)
fmt.Println("Decrypted:", string(decryptedData))
}
Hashing is essential for securely storing passwords. We'll use the bcrypt package to hash and verify passwords.
import (
"golang.org/x/crypto/bcrypt"
)
func hashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}
func verifyPassword(hashedPassword, password string) error {
return bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
}
func main() {
password := "securepassword123"
hashedPassword, _ := hashPassword(password)
fmt.Println("Hashed Password:", hashedPassword)
err := verifyPassword(hashedPassword, password)
if err != nil {
fmt.Println("Password verification failed")
} else {
fmt.Println("Password verified successfully")
}
}
crypto and bcrypt.In this tutorial, we explored how to implement encryption and decryption in Go using AES for symmetric encryption, RSA for asymmetric encryption, and bcrypt for password hashing. By following the best practices outlined, you can ensure that your applications handle sensitive data securely.
Remember, security is an ongoing process, and staying updated with the latest cryptographic standards and threats is crucial.