簡體   English   中英

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

[英]Encrypt in JS front end and decrypt in python backend using AES GCM

我正在嘗試使用 AES GCM 加密算法在 JS 前端加密並在 python 后端解密。 我將Web 密碼術 api用於 JS 前端,將 python 密碼庫用於 Z23EEEB4347BDD752BFC6B7EE9 密碼庫作為密碼庫。 我現在已經在兩側固定了 IV。 我在兩邊都實現了加密-解密代碼,它們在每一邊都工作。 但我認為填充的方式不同,似乎無法弄清楚在 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

如何解決這個填充問題?

GCM 是 stream 密碼模式,因此不需要填充。 加密時會隱式生成一個鑒權標簽,用於解密時的鑒權。 此外,建議 GCM 使用 12 字節的 IV/nonce。

與 JavaScript 代碼不同,發布的 Python 代碼不必要地填充並且沒有考慮身份驗證標簽,這可能是密文不同的主要原因。 這是否是唯一的原因,以及 JavaScript 代碼是否正確實現 GCM,很難說,因為getMessageEncoding()方法沒有發布,因此無法進行測試。

此外,兩個代碼都應用 16 字節 IV/nonce,而不是推薦的 12 字節 IV/nonce。


密碼學為 GCM 提供了兩種可能的實現。 一種實現使用非身份驗證模式的體系結構,如 CBC。 發布的 Python 代碼應用了這種設計,但沒有考慮身份驗證,因此沒有完全實現 GCM。 可以在此處找到此設計的正確示例。
密碼學通常推薦 GCM 的另一種方法(s. the Danger note),即AESGCM class,它執行隱式身份驗證,這樣就不會被意外忘記或錯誤地實現。

以下實現使用AESGCM class(並且還考慮了可選的附加身份驗證數據):

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:

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 模式尤其重要,例如此處 因此,通常為每個加密生成隨機 IV/nonce。


WebCrypto API 是用於密碼學的低級 API,不提供 Base64 編碼/解碼方法。 在下文中,為簡單起見,使用js-base64 就像 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>

使用 output:

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

其中密文與Python碼的密文相同。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM