1

密码学菜鸟在这里。我正在尝试在 NodeJS 中编写一个脚本来加密一个字符串并生成与我使用 cryptography.fernet 库的 Python 脚本的输出相匹配的输出。我的总体目标是使用原始密钥加密 Node 中的消息,这些消息稍后将使用 Python 解密。

我的 Python 代码示例:

from cryptography.fernet import Fernet

key = Fernet.generate_key() # For example: 6saGtiTFEXej729GUWSeAyQdIpRFdGhfY2XFUDpvsu8=
f = Fernet(key)
message = 'Hello World'
encoded = message.encode()
encrypted = f.encrypt(encoded)

产生输出:gAAAAABhJs_E-dDVp_UrLK6PWLpukDAM0OT5M6bfcqvVoCvg7r63NSi4OWOamLpABuYQG-5wsts_9h7cLbCsWmctArXcGqelXz_BXl_o2C7KM9o7_eq7VTc=

我的 Node 脚本使用内置 Crypto 模块,并且还必须使用与我的 Python 程序中使用的相同的 32 字节密钥。我知道 fernet 使用的是 AES-128-CBC 作为它的算法,所以这就是我在 Node 脚本中使用的。

我的 NodeJS 代码:

const crypto = require("crypto");

const key = '6saGtiTFEXej729GUWSeAyQdIpRFdGhfY2XFUDpvsu8=';
const algorithm = 'aes-128-cbc';
const message = 'Hello World';
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv(algorithm, key, iv);
const encrypted = cipher.update(message, 'utf8', 'hex') + cipher.final('hex');

这给了我:Error: Invalid key length

我的第一个问题是我不确定如何转换密钥以使其长度合适。通过查看 fernet 的源代码,我还知道密钥分为两部分:前 16 个字节是signing_key,最后 16 个字节是encryption_key- 我没有找到太多关于我是否需要/如何处理这两个字节的信息我的 Node 实现中的原始键的片段。

因为我是新手,所以我对如何完成我所追求的有点困惑。非常感谢任何提示或建议。

4

1 回答 1

0

Fernet 格式的规范可以在https://github.com/fernet/spec/blob/master/Spec.md上找到

他们在那里指定了生成和非常步骤,这里是生成应该为您的实现提供足够的信息:

  • 记录时间戳字段的当前时间。
  • 选择一个独特的 IV。
  • 构造密文:
    • 根据 RFC 5652 第 6.3 节,将消息填充为 16 字节(128 位)的倍数。这与 PKCS #7 v1.5 和所有版本的 SSL/TLS 中使用的填充技术相同(参见 RFC 5246,TLS 1.2 的第 6.2.3.2 节)。
    • 使用 CBC 模式下的 AES 128 使用所选 IV 和用户提供的加密密钥对填充消息进行加密。
  • 如上所述使用用户提供的签名密钥计算 HMAC 字段。
  • 以上述格式将所有字段连接在一起。
  • base64url 对整个令牌进行编码。

从这里我们可以看到,签名密钥(完整密钥的前半部分)用于 HMAC,而后半部分用于 AES128-CBC,因此只需将密钥分成两个单独的元素(从十六进制字符串正确转换为bytes) 应该足以使用 Node.js 加密模块 ( https://nodejs.org/en/knowledge/cryptography/how-to-use-crypto-module/ ) 来构建您自己的实现。

于 2021-08-28T22:54:56.290 回答