J'essaie de chiffrer dans le front-end JS et de déchiffrer dans le backend python à l'aide de l'algorithme cryptographique AES GCM. J'utilise l' API de cryptographie Web pour le front-end JS et la bibliothèque de cryptographie python pour le backend python en tant que bibliothèque cryptographique. J'ai corrigé le IV pour l'instant des deux côtés. J'ai implémenté le code de cryptage-décryptage des deux côtés, ils fonctionnent de chaque côté. Mais je pense que le remplissage est effectué différemment, je n'arrive pas à comprendre comment le remplissage est effectué dans l'API de cryptographie Web. Voici le chiffrement et le déchiffrement du backend Python :
def encrypt(derived_key, secret):
IV = bytes("ddfbccae-b4c4-11", encoding="utf-8")
aes = Cipher(algorithms.AES(derived_key), modes.GCM(IV))
encryptor = aes.encryptor()
padder = padding.PKCS7(128).padder()
padded_data = padder.update(secret.encode()) + padder.finalize()
return encryptor.update(padded_data) + encryptor.finalize()
def decrypt(derived_key, secret):
IV = bytes("ddfbccae-b4c4-11", encoding="utf-8")
aes = Cipher(algorithms.AES(derived_key), modes.GCM(IV))
decryptor = aes.decryptor()
decrypted_data = decryptor.update(secret)
unpadder = padding.PKCS7(128).unpadder()
return unpadder.update(decrypted_data) + unpadder.finalize()
Voici le code JS pour le chiffrement et le code de déchiffrement :
async function encrypt(secretKey, message) {
let iv = "ddfbccae-b4c4-11";
iv = Uint8Array.from(iv, x => x.charCodeAt(0))
let encoded = getMessageEncoding(message);
ciphertext = await window.crypto.subtle.encrypt(
{
name: "AES-GCM",
iv: iv
},
secretKey,
encoded
);
return ciphertext;
}
async function decrypt(secretKey, cipherText) {
iv = "ddfbccae-b4c4-11";
iv = Uint8Array.from(iv, x => x.charCodeAt(0))
try {
let decrypted = await window.crypto.subtle.decrypt(
{
name: "AES-GCM",
iv: iv
},
secretKey,
cipherText
);
let dec = new TextDecoder();
console.log("Decrypted message: ");
console.log(dec.decode(decrypted));
} catch (e) {
console.log("error");
}
}
J'essaie de chiffrer côté JS et de déchiffrer côté python. Mais j'ai l'erreur suivante :
Si j'essaie de crypter la même chaîne des deux côtés, j'obtiens ces sorties : En python, le texte crypté : \x17O\xadn\x11*I\x94\x99\xc6\x90\x8a\xa9\x9cc=
En JS le texte crypté : \x17O\xadn\x11*I\xdf\xe3F\x81(\x15\xcc\x8c^z\xdf+\x1d\x91K\xbc
Comment résoudre ce problème de remplissage ?
GCM est un mode de chiffrement de flux et ne nécessite donc pas de remplissage. Lors du cryptage, une balise d'authentification est implicitement générée, qui est utilisée pour l'authentification lors du décryptage. De plus, un IV/nonce de 12 octets est recommandé pour GCM.
Le code Python posté remplit inutilement et ne prend pas en compte la balise d'authentification, contrairement au code JavaScript, qui peut être la principale raison des différents textes chiffrés. Il est difficile de dire si c'est la seule raison et si le code JavaScript implémente correctement GCM, car la getMessageEncoding()
méthode n'a pas été publiée, il n'a donc pas été possible de tester cela.
De plus, les deux codes appliquent un IV/nonce de 16 octets au lieu des 12 octets IV/nonce recommandés.
La cryptographie offre deux implémentations possibles pour GCM. Une implémentation utilise l'architecture des modes de non-authentification comme CBC. Le code Python publié applique cette conception, mais ne prend pas en compte l'authentification et implémente donc GCM de manière incomplète. Un exemple correct pour cette conception peut être trouvé ici .
La cryptographie recommande généralement l'autre approche pour GCM (voir la note de danger ), à savoir la AESGCM
classe, qui effectue une authentification implicite afin que cela ne puisse pas être accidentellement oublié ou mal implémenté.
L'implémentation suivante utilise la AESGCM
classe (et prend également en compte les données authentifiées supplémentaires facultatives ) :
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
import base64
#import os
#key = AESGCM.generate_key(bit_length=256)
#nonce = os.urandom(12)
key = base64.b64decode('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE=') # fix for testing, AES-256
nonce = base64.b64decode('MDEyMzQ1Njc4OTAx') # fix for testing, 12 bytes
plaintext = b'The quick brown fox jumps over the lazy dog'
aad = b'the aad' # aad = None without additional authenticated data
aesgcm = AESGCM(key)
ciphertext = aesgcm.encrypt(nonce, plaintext, aad)
print('Ciphertext (B64): ' + base64.b64encode(ciphertext).decode('utf8'))
decrypted = aesgcm.decrypt(nonce, ciphertext, aad)
print('Decrypted: ' + decrypted.decode('utf8'))
avec la sortie :
Output
Ciphertext (B64): JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Decrypted: The quick brown fox jumps over the lazy dog
La balise d'authentification est ajoutée au texte chiffré, de sorte que le résultat (décodé en Base64) a la longueur du texte en clair (43 octets) plus la longueur de la balise (16 octets, par défaut), ce qui donne un total de 59 octets.
Pour les tests, une clé prédéfinie et IV/nonce sont utilisés en vue d'une comparaison avec le résultat du code JavaScript. Notez qu'en pratique une paire clé/IV ne peut être utilisée qu'une seule fois pour des raisons de sécurité, ce qui est particulièrement important pour le mode GCM, par exemple ici . Par conséquent, un IV/nonce aléatoire est généralement généré pour chaque chiffrement.
L' API WebCrypto est une API de bas niveau pour la cryptographie et ne fournit pas de méthodes pour l'encodage/décodage Base64. Dans ce qui suit, js-base64 est utilisé pour plus de simplicité. Tout comme le code Python, la balise est ajoutée au texte chiffré.
Une implémentation possible pour AES-GCM en utilisant la clé et IV/nonce du code Python qui est fonctionnellement essentiellement le même que le code JavaScript publié est :
(async () => {
var key = Base64.toUint8Array('MDEyMzQ1Njc4OTAxMjM0NTY3ODkwMTIzNDU2Nzg5MDE='); // fix for testing, AES-256
var nonce = Base64.toUint8Array('MDEyMzQ1Njc4OTAx'); // fix for testing, 12 bytes
var plaintext = new TextEncoder().encode("The quick brown fox jumps over the lazy dog");
var aad = new TextEncoder().encode('the aad');
var keyImported = await await crypto.subtle.importKey(
"raw",
key,
{ name: "AES-GCM" },
true,
["decrypt", "encrypt"]
);
var ciphertext = await await crypto.subtle.encrypt(
{ name: "AES-GCM", iv: nonce, additionalData: aad }, // { name: "AES-GCM", iv: nonce } without additional authenticated data
keyImported,
plaintext
);
console.log('Ciphertext (Base64):\n', Base64.fromUint8Array(new Uint8Array(ciphertext)).replace(/(.{48})/g,'$1\n'));
var decrypted = await await crypto.subtle.decrypt(
{ name: "AES-GCM", iv: nonce, additionalData: aad }, // { name: "AES-GCM", iv: nonce } without additional authenticated data
keyImported,
ciphertext
);
console.log('Plaintext:\n', new TextDecoder().decode(decrypted).replace(/(.{48})/g,'$1\n'));
})();
<script src="https://cdn.jsdelivr.net/npm/[email protected]/base64.min.js"></script>
avec la sortie :
Ciphertext (Base64):
JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Plaintext:
The quick brown fox jumps over the lazy dog
où le texte chiffré est le même que celui du code Python.
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