Le package crypto/aes de Go implémente l’algorithme symétrique AES. Il est couramment couplé à crypto/cipher pour gérer les modes d’opération. Les longueurs de clé acceptées sont 16, 24 ou 32 octets, correspondant respectivement à AES-128, AES-192 et AES-256.
Clé secrète
secret := []byte("une-clé-256-bits-32-octets-longue!")
Mode CBC avec bourrage PKCS7
CBC exige que le texte clair soit un multiple de la taille de bloc (16 octets). Le vecteur d’initialisation (IV) est généré aléatoirement et préfixé au chiffré.
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"errors"
"io"
)
func chiffreCBC(texte []byte, cle []byte) ([]byte, error) {
bloc, err := aes.NewCipher(cle)
if err != nil {
return nil, err
}
texte = bourragePKCS7(texte, aes.BlockSize)
sortie := make([]byte, aes.BlockSize+len(texte))
iv := sortie[:aes.BlockSize]
if _, err := io.ReadFull(rand.Reader, iv); err != nil {
return nil, err
}
mode := cipher.NewCBCEncrypter(bloc, iv)
mode.CryptBlocks(sortie[aes.BlockSize:], texte)
return sortie, nil
}
func dechiffreCBC(chiffre []byte, cle []byte) ([]byte, error) {
bloc, err := aes.NewCipher(cle)
if err != nil {
return nil, err
}
if len(chiffre) < aes.BlockSize {
return nil, errors.New("message trop court")
}
iv := chiffre[:aes.BlockSize]
chiffre = chiffre[aes.BlockSize:]
mode := cipher.NewCBCDecrypter(bloc, iv)
mode.CryptBlocks(chiffre, chiffre)
return supprimeBourragePKCS7(chiffre)
}
func bourragePKCS7(data []byte, tailleBloc int) []byte {
pad := tailleBloc - len(data)%tailleBloc
return append(data, bytes.Repeat([]byte{byte(pad)}, pad)...)
}
func supprimeBourragePKCS7(data []byte) ([]byte, error) {
longueur := len(data)
padLen := int(data[longueur-1])
if padLen > longueur {
return nil, errors.New("bourrage invalide")
}
return data[:longueur-padLen], nil
}
Mode GCM (chiffrement authentifié)
GCM combine confidentialité et intégrité sans bourrage manuel. Le nonce est généré aléatoirement puis accolé au chiffré.
import (
"crypto/cipher"
)
func chiffreGCM(texte []byte, cle []byte) ([]byte, error) {
bloc, err := aes.NewCipher(cle)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(bloc)
if err != nil {
return nil, err
}
nonce := make([]byte, gcm.NonceSize())
if _, err := rand.Read(nonce); err != nil {
return nil, err
}
return gcm.Seal(nonce, nonce, texte, nil), nil
}
func dechiffreGCM(chiffre []byte, cle []byte) ([]byte, error) {
bloc, err := aes.NewCipher(cle)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(bloc)
if err != nil {
return nil, err
}
tailleNonce := gcm.NonceSize()
if len(chiffre) < tailleNonce {
return nil, errors.New("message trop court")
}
nonce, texteChiffre := chiffre[:tailleNonce], chiffre[tailleNonce:]
return gcm.Open(nil, nonce, texteChiffre, nil)
}
Points de vgiilance
- Conserver la clé hors du code source (variables d’environnement, coffre-fort, etc.).
- Ne jamais réuitliser un IV ou un nonce ; générer une valeur aléatoire pour chaque opération.
- Privilégier GCM pour bénéficier de l’authentification intégrée et éviter la gestion du bourrage.