我尝试在JS前端加密,在python后端使用AES GCM加密算法解密。我将Web加密api用于JS前端,python密码库用于python后端作为加密库。我已经修好了两面的静脉输液。我在双方都实现了加解密码,它们在两边工作。但我认为填充是不同的,似乎无法理解在web加密api中填充是如何实现的。下面是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()以下是用于加密和解密代码的JS代码:
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");
}
}我试图在JS端加密,在python端解密。但我得到了以下错误:

如果我试图在两边加密相同的字符串,就会得到以下输出:在python中,加密文本:\x17O\xadn\x11*I\x94\x99\xc6\x90\x8a\xa9\x9cc=
在JS中加密的文本:\x17O\xadn\x11*I\xdf\xe3F\x81(\x15\xcc\x8c^z\xdf+\x1d\x91K\xbc
如何解决这个填充问题?
发布于 2021-06-14 07:55:00
GCM是一种流密码模式,因此不需要填充。加密期间,隐式生成身份验证标记,用于解密期间的身份验证。此外,GCM建议使用12字节的IV/nonce。
发布的Python代码不必要地填充并且不考虑身份验证标记,与JavaScript代码不同,这可能是产生不同密文的主要原因。这是否是唯一的原因,以及JavaScript代码是否正确地实现了GCM,这很难说,因为getMessageEncoding()方法没有发布,因此测试这是不可能的。
另外,这两种代码都应用了16字节IV/nonce,而不是推荐的12字节IV/nonce。
密码学为GCM提供了两种可能的实现。一个实现使用非身份验证模式(如CBC )的体系结构。发布的Python代码应用了这个设计,但是没有考虑到身份验证,因此不完全实现GCM。有关此设计的正确示例可以找到这里。
密码学通常建议GCM (s. https://cryptography.io/en/latest/hazmat/primitives/symmetric-encryption/#cryptography.hazmat.primitives.ciphers.modes.GCM注释)的另一种方法,即AESGCM类,它执行隐式身份验证,从而不会意外地忘记或不正确地实现。
以下实现使用AESGCM类(还考虑到可选的附加身份验证数据):
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'))产出如下:
Output
Ciphertext (B64): JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Decrypted: The quick brown fox jumps over the lazy dog身份验证标记被附加到密文中,因此(Base64解码的)结果具有明文的长度(43字节)加上标记的长度(16字节,默认值),总共为59字节。
为了进行测试,在与JavaScript代码的结果进行比较时,使用了预定义的密钥和IV/nonce。注意,在实践中,由于安全原因,密钥/IV对只能使用一次,这对于GCM模式尤其重要,例如这里。因此,通常为每种加密生成随机IV/nonce。
WebCrypto API是一种用于加密的低级API,不提供Base64编码/解码方法。在下面的文章中,为了简单起见,使用了js 64。就像Python代码一样,标记被附加到密文中。
使用Python的键和IV/nonce实现AES-GCM的一个可能的实现,在功能上与发布的JavaScript代码基本相同:
(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/js-base64@3.2.4/base64.min.js"></script>
产出如下:
Ciphertext (Base64):
JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Plaintext:
The quick brown fox jumps over the lazy dog其中密文与Python代码相同。
https://stackoverflow.com/questions/67963529
复制相似问题