使用AES GCM在JS前端加密,在python后端解密

2024-10-03 04:37:31 发布

您现在位置:Python中文网/ 问答频道 /正文

我尝试在JS前端加密,在python后端使用AES GCM加密算法解密。我使用Web cryptography api作为JS前端,使用python cryptography library作为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端解密。但我得到了以下错误: enter image description here

如果我尝试在两侧加密相同的字符串,我会得到以下输出: 在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

如何解决这个填充问题


Tags: keydatasecretjsupdateaesgcmencryptor
1条回答
网友
1楼 · 发布于 2024-10-03 04:37:31

GCM是一种流密码模式,因此不需要填充。在加密期间,隐式生成一个身份验证标记,用于解密期间的身份验证。此外,建议GCM使用12字节的IV/nonce

与JavaScript代码不同,发布的Python代码不必要地填充并没有考虑身份验证标记,这可能是产生不同密文的主要原因。这是否是唯一的原因以及JavaScript代码是否正确实现了GCM,很难说,因为getMessageEncoding()方法没有发布,所以测试这一点是不可能的

此外,这两种代码都应用16字节的IV/nonce,而不是推荐的12字节IV/nonce


加密为GCM提供了两种可能的实现。一个实现使用非认证模式(如CBC)的体系结构。发布的Python代码应用了这种设计,但没有考虑身份验证,因此未完全实现GCM。此设计的正确示例可以在here中找到。
密码学通常推荐GCM的另一种方法(如Danger注释),即^{}类,它执行隐式身份验证,以防止意外忘记或错误实现

以下实现使用了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字节

对于测试,使用预定义键和IV/nonce与JavaScript代码的结果进行比较。请注意,实际上,出于安全原因,密钥/IV对只能使用一次,这对于GCM模式尤其重要,例如here。因此,通常为每个加密生成随机IV/nonce


WebCryptoAPI是用于加密的低级API,不提供Base64编码/解码方法。下面使用js-base64是为了简单。与Python代码一样,标记被附加到密文中

AES-GCM的一个可能实现是使用Python代码的key和IV/nonce,该代码在功能上与发布的JavaScript代码基本相同:

&13; 第13部分,;
(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>
和#13;
和#13;

对于输出:

Ciphertext (Base64):
 JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Plaintext:
 The quick brown fox jumps over the lazy dog

其中密文与Python代码的密文相同

相关问题 更多 >