这个用于解密 AES-CCM 加密字符串的 JS 函数的 Python 等效项是什么?

aar*_*nk6 3 javascript python encryption aes sjcl

我\xe2\x80\x99d喜欢在Python 3中解密AES加密的字符串(CCM模式)。

\n

以下 JavaScript 代码使用sjcl库的 JavaScript 代码可以正常工作:

\n
const sjcl = require(\'sjcl\');\n\nconst key = "ef530e1d82c154170296467bfe40cdb47b9ad77e685bbf8336b145dfa0e85640";\nconst keyArray = sjcl.codec.hex.toBits(key);\nconst iv = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(key.substr(0,16))); \nconst params = {\n    "iv": iv,\n    "v": 1,\n    "iter": 1000,\n    "ks": 256,\n    "ts": 128,\n    "mode": "ccm",\n    "adata": "",\n    "cipher": "aes",\n    "salt": "",\n};\n\nfunction encrypt(data) {\n    const ct = JSON.parse(sjcl.encrypt(keyArray, data, params)).ct;\n    return sjcl.codec.hex.fromBits(sjcl.codec.base64.toBits(ct));\n}\n\nfunction decrypt(data) {\n    const ct = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(data));\n    const paramsWithCt = JSON.stringify({ ...params, ...{ "ct": ct } });\n    return sjcl.decrypt(keyArray, paramsWithCt);\n}\n\nlet ct = encrypt("my secret string");\nconsole.log("Cipher Text: " + ct);\n\nlet plain = decrypt(ct);\nconsole.log("Plain Text: " + plain);\n\n
Run Code Online (Sandbox Code Playgroud)\n

输出:

\n
$ npm i sjcl\n$ node index.js\nCipher Text: fa90bcdedbfe7ba89b69216e352a90fa57a63871fc4da7e69ab7f897f427f8e3\nPlain Text: my secret string\n
Run Code Online (Sandbox Code Playgroud)\n

我可以使用哪个库在 Python 中执行相同的操作?

\n

我尝试使用pycryptodome 库,但它接受一组不同的参数:

\n
    \n
  • 钥匙 (bytes) \xe2\x80\x93 加密密钥
  • \n
  • 模式\xe2\x80\x93 常量 Crypto.Cipher.<算法>.MODE_CCM
  • \n
  • 随机数 (字节) \xe2\x80\x93 固定随机数的值。对于组合消息/密钥来说它必须是唯一的。对于 AES,其长度从 7 到 13 个字节不等。nonce 越长,允许的消息大小越小(nonce 为 13 字节时,消息不能超过 64KB)。如果不存在,该库将创建一个 11 字节随机数(最大消息大小为 8GB)。
  • \n
  • mac_len(整数)\xe2\x80\x93 MAC 标记的所需长度(如果不存在则默认:16 字节)。
  • \n
  • 消息长度 (整数) \xe2\x80\x93 预声明要加密的消息长度。如果未指定,则 encrypt() 和 decrypt() 只能调用一次。
  • \n
  • 关联长度 (整数) \xe2\x80\x93 关联数据长度的预声明。如果未指定,内部将进行一些额外的缓冲。
  • \n
\n

Top*_*aco 5

sjcl对 4 字节字的数组进行操作sjcl.codec.hex.toBits()十六进制编码的密钥被转换成这样的数组。密钥的前 8 个字节(16 个十六进制数字)用作随机数。
密钥大小、标签大小、算法和模式由对象确定params。该params对象还包含用于密钥派生的参数,例如itersalt等),但此处将忽略这些参数,因为密钥作为数组而不是字符串传递。
Nonce 和密文在对象内以 Base64 编码传递params

密文是实际密文和标签按此顺序串联而成,也必须以这种格式传递到解密。sjcl处理连接的密文和标签,
PyCryptodome分别处理两者。除此之外,Python 中的加密和解密使用PyCryptodome非常简单:

from Crypto.Cipher import AES

data = b'my secret string'
key = bytes.fromhex('ef530e1d82c154170296467bfe40cdb47b9ad77e685bbf8336b145dfa0e85640')
nonce = bytes.fromhex('ef530e1d82c154170296467bfe40cdb47b9ad77e685bbf8336b145dfa0e85640')[:8]

# Encryption 
cipher = AES.new(key, AES.MODE_CCM, nonce)
ciphertext, tag = cipher.encrypt_and_digest(data)

ciphertextTagHex = ciphertext.hex() + tag.hex()
print(ciphertextTagHex) # fa90bcdedbfe7ba89b69216e352a90fa57a63871fc4da7e69ab7f897f427f8e3

# Decryption
ciphertextTag = bytes.fromhex(ciphertextTagHex)
ciphertext = ciphertextTag[:-16]
tag = ciphertextTag[-16:]

cipher = AES.new(key, AES.MODE_CCM, nonce)
try:
    decrypted = cipher.decrypt_and_verify(ciphertext, tag)
    print(decrypted.decode('utf-8')) # my secret string
except ValueError:
    print('Decryption failed')
Run Code Online (Sandbox Code Playgroud)

请注意,从密钥导出随机数是不安全的。对于 CCM、seg RFC4309、p.1来说尤其如此。3、最后一节

AES CCM 采用计数器模式进行加密。与任何流密码一样,使用相同的密钥重复使用相同的 IV 值是灾难性的。

相反,应该为每次加密随机生成随机数。随机数不是秘密的,通常与字节级别的密文连接起来,通常是nonce|ciphertext|tag