You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让Python中的Fernet正确接受密钥?

如何让Python中的Fernet正确接受密钥?

我帮你分析下问题出在哪,你现在的Fernet密钥错误主要是三个核心问题:密钥格式理解偏差、AES加密未做填充导致密钥损坏,还有压缩包加密顺序错误,一步步拆解来看:

问题1:完全搞反了Fernet密钥的格式要求

Fernet.generate_key()生成的已经是符合Fernet要求的密钥——它是32字节原始密钥经过URL-safe Base64编码后的bytes(长度通常是44字节,带=填充),比如类似b'abcdefghijklmnopqrstuvwxyzABCDEFG='这样的格式,直接拿来初始化Fernet就行。

但你在解密代码里做了多余的操作:

y_base64 = base64.urlsafe_b64encode(y).decode('utf-8')
y_decoded = base64.urlsafe_b64decode(y_base64)

这相当于把已经是Base64编码的合法密钥,又重复编码再解码,最后得到的是原始32字节随机数据,完全不符合Fernet的密钥格式要求。

问题2:AES加密Fernet密钥时未做填充,导致密钥损坏

你用AES-CBC加密y(Fernet生成的密钥),但CBC模式要求加密数据长度必须是AES块大小(16字节)的整数倍。Fernet.generate_key()返回的y是44字节,44%16=12,不是16的倍数,你又没加填充,所以加密时要么直接报错,要么解密后得到的y被截断/损坏(比如你日志里的y长度是32字节,明显不是正常的Fernet密钥长度)。

问题3:ZIP压缩包的加密顺序完全错误

你现在的代码是先把文件写入压缩包,再设置密码和加密方式,这在PyZipper里是无效的——必须先设置加密方式和密码,再添加文件,否则压缩包根本不会被正确加密。


修复后的完整代码

加密端代码

import os
import base64
from cryptography.fernet import Fernet
import pyzipper
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding

filename = "你要加密的文件路径"
archname = "输出的压缩包路径.zip"

# 生成ZIP密码(16字节,符合AES块大小)
x = os.urandom(16)
# 生成Fernet密钥——注意:这个y已经是符合Fernet要求的URL-safe Base64编码格式
y = Fernet.generate_key()

# 修复ZIP加密顺序:先初始化加密设置,再添加文件
try:
    with pyzipper.AESZipFile(archname, 'w', compression=pyzipper.ZIP_LZMA) as zip_file:
        # 先设置加密方式和密码
        zip_file.setencryption(pyzipper.WZ_AES, nbits=256)
        zip_file.setpassword(x)
        
        # 再添加文件到压缩包
        zip_file.write(filename, arcname=os.path.basename(filename))

    print(f"文件 {filename} 已成功添加到加密压缩包。")
except Exception as e:
    print(f"ZIP操作错误: {e}")
    exit(1)

# 用Fernet加密整个ZIP包
print("加密压缩包: 7/10")
try:
    cipher_suite = Fernet(y)
    with open(archname, 'rb') as file:
        file_data = file.read()
        encrypted_data = cipher_suite.encrypt(file_data)

    with open(archname, 'wb') as afile:
        afile.write(encrypted_data)

    print("压缩包已通过Fernet成功加密。")
except Exception as e:
    print(f"Fernet加密错误: {e}")
    exit(1)

# 用AES-CBC加密ZIP密码x和Fernet密钥y,添加PKCS7填充处理非块大小数据
print("加密传输密钥: 9/10")
key = os.urandom(32)  # AES-256密钥
iv = os.urandom(16)   # CBC初始化向量

# 初始化AES-CBC加密器和PKCS7填充器
cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
padder = padding.PKCS7(128).padder()  # 128位块大小对应AES

# 加密x(16字节,刚好块大小,填充后不变)
encryptor = cipher.encryptor()
padded_x = padder.update(x) + padder.finalize()
aesx = encryptor.update(padded_x) + encryptor.finalize()

# 重新初始化填充器和加密器(每个加密器只能使用一次)
padder = padding.PKCS7(128).padder()
encryptor = cipher.encryptor()
# 加密y(44字节,需要填充)
padded_y = padder.update(y) + padder.finalize()
aesy = encryptor.update(padded_y) + encryptor.finalize()

# 输出需要传输的参数
print(f"aesx: {base64.b64encode(aesx).decode()}")
print(f"aesy: {base64.b64encode(aesy).decode()}")
print(f"key: {base64.b64encode(key).decode()}")
print(f"iv: {base64.b64encode(iv).decode()}")

解密端代码

import os
import base64
from cryptography.fernet import Fernet
import pyzipper
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding

archname = "要解密的压缩包路径.zip"
extract_path = "解压输出路径"

def decrypt_data(aesx, aesy, key, iv):
    cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend())
    unpadder = padding.PKCS7(128).unpadder()

    # 解密aesx得到ZIP密码x
    decryptor = cipher.decryptor()
    padded_x = decryptor.update(aesx) + decryptor.finalize()
    x = unpadder.update(padded_x) + unpadder.finalize()

    # 重新初始化解密器和去填充器
    decryptor = cipher.decryptor()
    unpadder = padding.PKCS7(128).unpadder()
    # 解密aesy得到Fernet密钥y
    padded_y = decryptor.update(aesy) + decryptor.finalize()
    y = unpadder.update(padded_y) + unpadder.finalize()

    return x, y

# 输入传输的加密参数
aesx = base64.b64decode(input("aesx: "))
aesy = base64.b64decode(input("aesy: "))
key = base64.b64decode(input("key: "))
iv = base64.b64decode(input("iv: "))

# 解密得到ZIP密码x和Fernet密钥y
x, y = decrypt_data(aesx, aesy, key, iv)

print(f"解密后的Fernet密钥: {y}")
print(f"Fernet密钥长度: {len(y)}")  # 正常应为44字节左右

# 直接用y初始化Fernet!无需额外Base64操作
try:
    cipher_suite = Fernet(y)
    with open(archname, 'rb') as encrypted_file:
        encrypted_data = encrypted_file.read()
        decrypted_data = cipher_suite.decrypt(encrypted_data)

    with open(archname, 'wb') as decrypted_file:
        decrypted_file.write(decrypted_data)

    print("压缩包已通过Fernet成功解密。")
except Exception as e:
    print(f"Fernet解密错误: {e}")
    exit(1)

# 解密ZIP包并提取文件
try:
    with pyzipper.AESZipFile(archname, 'r') as zip_file:
        # 先设置密码,再提取文件
        zip_file.setpassword(x)
        zip_file.extractall(extract_path)
    print(f"压缩包已成功解压到 {extract_path}")
except Exception as e:
    print(f"ZIP解压错误: {e}")
    exit(1)

关键修复点总结

  1. Fernet密钥直接使用Fernet.generate_key()返回的y已经是合法密钥,解密后直接拿来初始化Fernet,不要再做任何Base64编码/解码操作。
  2. AES加密必须加填充:用PKCS7填充处理非块大小倍数的数据,避免密钥损坏。
  3. ZIP加密顺序要正确:先设置加密方式和密码,再添加/提取文件,否则加密逻辑无效。

你可以试试上面的代码,应该就能解决Fernet密钥错误的问题了。

备注:内容来源于stack exchange,提问作者kolya080808

火山引擎 最新活动