这个用于解密 AES-CCM 加密字符串的 JS 函数的 Python 等价物是什么?
What is the Python equivalent of this JS function to decrypt an AES-CCM-encrypted string?
我想在 Python 3.
中解密 AES 加密字符串(CCM 模式)
以下使用 sjcl 库的 JavaScript 代码工作正常:
const sjcl = require('sjcl');
const key = "ef530e1d82c154170296467bfe40cdb47b9ad77e685bbf8336b145dfa0e85640";
const keyArray = sjcl.codec.hex.toBits(key);
const iv = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(key.substr(0,16)));
const params = {
"iv": iv,
"v": 1,
"iter": 1000,
"ks": 256,
"ts": 128,
"mode": "ccm",
"adata": "",
"cipher": "aes",
"salt": "",
};
function encrypt(data) {
const ct = JSON.parse(sjcl.encrypt(keyArray, data, params)).ct;
return sjcl.codec.hex.fromBits(sjcl.codec.base64.toBits(ct));
}
function decrypt(data) {
const ct = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(data));
const paramsWithCt = JSON.stringify({ ...params, ...{ "ct": ct } });
return sjcl.decrypt(keyArray, paramsWithCt);
}
let ct = encrypt("my secret string");
console.log("Cipher Text: " + ct);
let plain = decrypt(ct);
console.log("Plain Text: " + plain);
输出:
$ npm i sjcl
$ node index.js
Cipher Text: fa90bcdedbfe7ba89b69216e352a90fa57a63871fc4da7e69ab7f897f427f8e3
Plain Text: my secret string
我可以使用哪个库在 Python 中执行相同的操作?
我尝试使用 pycryptodome library,但它接受一组不同的参数:
- key(字节)——加密密钥
- mode – 常量 Crypto.Cipher.
.MODE_CCM
- nonce (bytes) – 固定随机数的值。它对于组合 message/key 必须是唯一的。对于 AES,其长度从 7 到 13 个字节不等。 nonce 越长,允许的消息大小越小(nonce 为 13 字节,消息不能超过 64KB)。如果不存在,库会创建一个 11 字节的随机随机数(最大消息大小为 8GB)。
- mac_len(整数)– MAC 标签的所需长度(如果不存在则默认:16 字节)。
- msg_len(整数)——预先声明要加密的消息长度。如果不指定,encrypt()和decrypt()只能调用一次。
- assoc_len(整数)——预声明关联数据的长度。如果未指定,将在内部进行一些额外的缓冲。
sjcl 对 4 字节字数组进行操作。使用 sjcl.codec.hex.toBits()
十六进制编码的密钥被转换成这样的数组。密钥的前 8 个字节(16 个十六进制数字)用作随机数。
密钥大小、标签大小、算法和模式由 params
对象确定。 params
对象还包含用于密钥派生的参数,例如iter
、salt
等),但这些在这里被忽略,因为键是作为数组而不是字符串传递的。
Nonce 和密文在 params
对象中通过 Base64 编码传递。
密文是实际密文和标签按顺序拼接而成,也必须按此格式传递给解密
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')
请注意,从密钥中导出随机数是不安全的。对于 CCM,s 尤其如此。例如RFC4309, p. 3, last section:
AES CCM employs counter mode for encryption. As with any stream
cipher, reuse of the same IV value with the same key is catastrophic.
相反,应为每次加密随机生成随机数。 nonce 不是秘密的,通常在字节级别与密文连接,通常为 nonce|ciphertext|tag
.
我想在 Python 3.
中解密 AES 加密字符串(CCM 模式)以下使用 sjcl 库的 JavaScript 代码工作正常:
const sjcl = require('sjcl');
const key = "ef530e1d82c154170296467bfe40cdb47b9ad77e685bbf8336b145dfa0e85640";
const keyArray = sjcl.codec.hex.toBits(key);
const iv = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(key.substr(0,16)));
const params = {
"iv": iv,
"v": 1,
"iter": 1000,
"ks": 256,
"ts": 128,
"mode": "ccm",
"adata": "",
"cipher": "aes",
"salt": "",
};
function encrypt(data) {
const ct = JSON.parse(sjcl.encrypt(keyArray, data, params)).ct;
return sjcl.codec.hex.fromBits(sjcl.codec.base64.toBits(ct));
}
function decrypt(data) {
const ct = sjcl.codec.base64.fromBits(sjcl.codec.hex.toBits(data));
const paramsWithCt = JSON.stringify({ ...params, ...{ "ct": ct } });
return sjcl.decrypt(keyArray, paramsWithCt);
}
let ct = encrypt("my secret string");
console.log("Cipher Text: " + ct);
let plain = decrypt(ct);
console.log("Plain Text: " + plain);
输出:
$ npm i sjcl
$ node index.js
Cipher Text: fa90bcdedbfe7ba89b69216e352a90fa57a63871fc4da7e69ab7f897f427f8e3
Plain Text: my secret string
我可以使用哪个库在 Python 中执行相同的操作?
我尝试使用 pycryptodome library,但它接受一组不同的参数:
- key(字节)——加密密钥
- mode – 常量 Crypto.Cipher.
.MODE_CCM - nonce (bytes) – 固定随机数的值。它对于组合 message/key 必须是唯一的。对于 AES,其长度从 7 到 13 个字节不等。 nonce 越长,允许的消息大小越小(nonce 为 13 字节,消息不能超过 64KB)。如果不存在,库会创建一个 11 字节的随机随机数(最大消息大小为 8GB)。
- mac_len(整数)– MAC 标签的所需长度(如果不存在则默认:16 字节)。
- msg_len(整数)——预先声明要加密的消息长度。如果不指定,encrypt()和decrypt()只能调用一次。
- assoc_len(整数)——预声明关联数据的长度。如果未指定,将在内部进行一些额外的缓冲。
sjcl 对 4 字节字数组进行操作。使用 sjcl.codec.hex.toBits()
十六进制编码的密钥被转换成这样的数组。密钥的前 8 个字节(16 个十六进制数字)用作随机数。
密钥大小、标签大小、算法和模式由 params
对象确定。 params
对象还包含用于密钥派生的参数,例如iter
、salt
等),但这些在这里被忽略,因为键是作为数组而不是字符串传递的。
Nonce 和密文在 params
对象中通过 Base64 编码传递。
密文是实际密文和标签按顺序拼接而成,也必须按此格式传递给解密
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')
请注意,从密钥中导出随机数是不安全的。对于 CCM,s 尤其如此。例如RFC4309, p. 3, last section:
AES CCM employs counter mode for encryption. As with any stream cipher, reuse of the same IV value with the same key is catastrophic.
相反,应为每次加密随机生成随机数。 nonce 不是秘密的,通常在字节级别与密文连接,通常为 nonce|ciphertext|tag
.