如何让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)
关键修复点总结
- Fernet密钥直接使用:
Fernet.generate_key()返回的y已经是合法密钥,解密后直接拿来初始化Fernet,不要再做任何Base64编码/解码操作。 - AES加密必须加填充:用PKCS7填充处理非块大小倍数的数据,避免密钥损坏。
- ZIP加密顺序要正确:先设置加密方式和密码,再添加/提取文件,否则加密逻辑无效。
你可以试试上面的代码,应该就能解决Fernet密钥错误的问题了。
备注:内容来源于stack exchange,提问作者kolya080808




