首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用AES GCM在JS前端加密并在python后端解密

用AES GCM在JS前端加密并在python后端解密
EN

Stack Overflow用户
提问于 2021-06-14 00:07:42
回答 1查看 1.7K关注 0票数 1

我尝试在JS前端加密,在python后端使用AES GCM加密算法解密。我将Web加密api用于JS前端,python密码库用于python后端作为加密库。我已经修好了两面的静脉输液。我在双方都实现了加解密码,它们在两边工作。但我认为填充是不同的,似乎无法理解在web加密api中填充是如何实现的。下面是python后端的加密和解密:

代码语言:javascript
复制
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代码:

代码语言:javascript
复制
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

如何解决这个填充问题?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 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类(还考虑到可选的附加身份验证数据):

代码语言:javascript
复制
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'))

产出如下:

代码语言:javascript
复制
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代码基本相同:

代码语言: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'));
})();
代码语言:javascript
复制
<script src="https://cdn.jsdelivr.net/npm/js-base64@3.2.4/base64.min.js"></script>

产出如下:

代码语言:javascript
复制
Ciphertext (Base64):
 JOetStCANhPISvQ6G6IcRBauqbtC8fzRooblayHqkqSPKzLbidx/gBWfLNzBC+ZpcAGnGnHXaI7CB1U=
Plaintext:
 The quick brown fox jumps over the lazy dog

其中密文与Python代码相同。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67963529

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档