J'essaie d'implémenter une structure (le CryptoService
) cachant en / décryptage du flux principal du programme. J'ai implémenté des fonctions "normales" et des variantes en base64 qui devraient coder le chiffrement en son équivalent en base64 et vice-versa lors du déchiffrement. Ceci est fait parce que notre protocole de réseau interne utilise le saut de ligne \n
comme délimiteur.
Voir code d'implémentation ci-dessous
Après avoir écrit le code ci-dessous, j'ai commencé à le tester. Au début , il est bien passé et a fonctionné et le déchiffrement en- mais bientôt je commencé à remarquer des erreurs « apparaissant au hasard » au cours du processus de décryptage: cipher: message authentication failed
. Maintenant, le fait important: les erreurs retournées UNIQUEMENT par le DecryptBase64
func. Mais l'utilisation de base64 dans go est assez simple et il n'y a pas grand-chose à craindre, donc je n'ai aucune idée d'où se trouvent les problèmes.
Ci-dessous, vous voyez le code de mon CryptoService
implémentation et le fichier de test associé. J'ai essayé de nettoyer le code autant que possible (supprimer les commentaires, les vérifications d'entrée supplémentaires, etc.) sans supprimer le contexte. Néanmoins, c'est beaucoup de code, donc merci à tous ceux qui l'ont lu - appréciez vraiment votre aide!
cryptoservice.go
type CryptoService struct {
gcm cipher.AEAD
}
func NewCryptoService(key []byte) (cs *CryptoService, err error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
return &CryptoService{
gcm: gcm,
}, nil
}
func (cs CryptoService) Encrypt(plain []byte) (cipher []byte, err error) {
nonce := make([]byte, cs.gcm.NonceSize())
_, err = io.ReadFull(rand.Reader, nonce)
if err != nil {
return nil, err
}
cipher = cs.gcm.Seal(nil, nonce, plain, nil)
cipher = append(nonce, cipher...)
return cipher, nil
}
func (cs CryptoService) EncryptBase64(plain []byte) (base64Cipher []byte, err error) {
cipher, err := cs.Encrypt(plain)
if err != nil {
return nil, err
}
base64Cipher = make([]byte, base64.StdEncoding.EncodedLen(len(cipher)))
base64.StdEncoding.Encode(base64Cipher, cipher)
return
}
func (cs CryptoService) Decrypt(cipher []byte) (plain []byte, err error) {
nonce := cipher[0:cs.gcm.NonceSize()]
ciphertext := cipher[cs.gcm.NonceSize():]
plain, err = cs.gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, err
}
return
}
func (cs CryptoService) DecryptBase64(base64Cipher []byte) (plain []byte, err error) {
cipher := make([]byte, base64.StdEncoding.DecodedLen(len(base64Cipher)))
_, err = base64.StdEncoding.Decode(cipher, base64Cipher)
if err != nil {
return nil, err
}
return cs.Decrypt(cipher)
}
cryptoservice_test.go
TestCryptoService_EncryptDecryptRoundtrip
fonctionne bienTestCryptoService_EncryptBase64DecryptBase64Roundtrip
échoue "parfois" (notez également qu'il n'échoue pas toujours sur les mêmes cas de test)Informations supplémentaires: La FastRandomString(n int)
fonction de création de cas de test dynamique n'est en fait qu'un copier-coller de la réponse acceptée à: Comment générer une chaîne aléatoire d'une longueur fixe en golang?
func TestCryptoService_EncryptDecryptRoundtrip(t *testing.T) {
tests := []struct {
name string
aeskey string
text string
}{
{"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
{"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
{"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
{"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
{"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
{"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
{"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for i := 0; i < 1000; i++ {
key, _ := hex.DecodeString(tt.aeskey)
cs, _ := NewCryptoService(key)
cipher, err := cs.Encrypt([]byte(tt.text))
if err != nil {
t.Errorf("CryptoService.Encrypt() error = %v", err)
return
}
plain, err := cs.Decrypt(cipher)
if err != nil {
t.Errorf("CryptoService.Decrypt() error = %v", err)
return
}
plainStr := string(plain)
if plainStr != tt.text {
t.Errorf("CryptoService.Decrypt() plain = %v, want = %v", plainStr, tt.text)
return
}
}
})
}
}
func TestCryptoService_EncryptBase64DecryptBase64Roundtrip(t *testing.T) {
tests := []struct {
name string
aeskey string
text string
}{
{"Simple 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Text"},
{"Simple 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Some random content"},
{"Simple 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua."},
{"Dynamic 1", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(32)},
{"Dynamic 2", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024)},
{"Dynamic 3", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 64)},
{"Dynamic 4", "c4cc0dfc4ae0e45c045727f84ffd373127453bc232230bf1386972ac692436c1", FastRandomString(1024 * 256)},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
for i := 0; i < 1000; i++ {
key, _ := hex.DecodeString(tt.aeskey)
cs, _ := NewCryptoService(key)
cipher, err := cs.EncryptBase64([]byte(tt.text))
if err != nil {
t.Errorf("CryptoService.EncryptBase64() error = %v", err)
return
}
plain, err := cs.DecryptBase64(cipher)
if err != nil {
t.Errorf("CryptoService.DecryptBase64() error = %v", err)
return
}
plainStr := string(plain)
if plainStr != tt.text {
t.Errorf("CryptoService.DecryptBase64() plain = %v, want = %v", plainStr, tt.text)
return
}
}
})
}
}
Un membre de la communauté GopherSlack a proposé la solution:
StdEncoding
tampons ses résultats qui dans ce cas provoquaient des problèmes de décryptage lorsque (pendant le processus de cryptage) un remplissage de la sortie était nécessaire. Par conséquent, vous devez utiliser RawStdEncoding
dans cet exemple.
Merci pour l'aide! :)
Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.
En cas d'infraction, veuillez [email protected] Supprimer.
laisse moi dire quelques mots