Python中AES密钥字节数组转字符串及文件写入问题求助
问题根源与解决方案
核心问题:Python中字节(bytes)与字符串(str)的严格区分
C#中字节数组转Base64直接得到字符串,但Python里b64encode返回的是bytes类型,必须显式转为字符串才能和普通字符串拼接、写入文本文件。另外,随机生成的密钥字节本身不是合法UTF-8编码,直接decode("utf-8")必然报错。
逐个解决你的问题
- 解码UTF-8报错:随机密钥是二进制数据,不是UTF-8编码的文本,不能直接
decode。正确做法是用Base64编码将二进制转为可打印字符串。 - 换行分割时类型错误:
b64encode返回bytes,'\n'.join()要求元素是字符串,需先将Base64 bytes转为str(用decode("utf-8"))。 - 字符串与bytes拼接报错:同样是类型不匹配,先把Base64 bytes转成str再拼接。
- 写入文件时类型错误:
open(file, 'w')模式只接受字符串,要么把Base64 bytes转str,要么用'wb'模式写二进制,但这里要和C#一致写文本,所以转str后写入。
修正后的Python实现(匹配C#功能)
先实现和你C#代码完全一致的密钥/IV生成、Base64编码、写入文件功能,再修正你原类中的错误:
1. 对应C#的核心功能实现
from tkinter.filedialog import asksaveasfilename from Crypto.Random import get_random_bytes from base64 import b64encode # 生成256位AES密钥和IV(和C#的KeySize=256对应) aes_key = get_random_bytes(32) # 256位=32字节 aes_iv = get_random_bytes(16) # 转Base64字符串(注意b64encode返回bytes,需decode成str) key_b64 = b64encode(aes_key).decode("utf-8") iv_b64 = b64encode(aes_iv).decode("utf-8") # 构造文件内容,和C#格式一致 file_text = f"----- Start Key File -----\n<0>{key_b64}</0>\n<1>{iv_b64}</1>\n----- End Key File -----" # 保存文件 file_path = asksaveasfilename( filetypes=[("SEA file", "*.sea")], defaultextension=".sea" ) if file_path: with open(file_path, 'w', encoding="utf-8") as f: f.write(file_text)
2. 修正你的Crypton类(解决加密解密的字节/字符串处理错误)
你的原类中错误地将二进制数据(ciphertext、tag、nonce)直接用UTF-8解码/编码,这会导致数据损坏,正确做法是用Base64处理二进制数据:
from tkinter.filedialog import asksaveasfilename, askopenfilename from Crypto.Cipher import AES from Crypto.Random import get_random_bytes from base64 import b64encode, b64decode class Crypton: @staticmethod def GetKey(): # 生成16字节密钥,转Base64字符串返回(不再直接decode UTF-8) return b64encode(get_random_bytes(16)).decode("utf-8") @staticmethod def Encrypt(data, key_b64): # 把Base64字符串密钥转回字节 key = b64decode(key_b64) cipher = AES.new(key, AES.MODE_EAX) ciphertext, tag = cipher.encrypt_and_digest(data) nonce = cipher.nonce # 所有二进制数据转Base64字符串后返回 return [ key_b64, b64encode(ciphertext).decode("utf-8"), b64encode(tag).decode("utf-8"), b64encode(nonce).decode("utf-8") ] @staticmethod def Decrypt(str_data): # 把Base64字符串转回字节 key = b64decode(str_data[0]) ciphertext = b64decode(str_data[1]) tag = b64decode(str_data[2]) nonce = b64decode(str_data[3]) cipher = AES.new(key, AES.MODE_EAX, nonce) return cipher.decrypt_and_verify(ciphertext, tag) @staticmethod def CreateCryptonKeyFile(key_b64): bricked_key = Crypton.BreakKey(key_b64) text = f"------ Crypton Key Begin ------\n{bricked_key}\n------ Crypton Key End ------" file = asksaveasfilename( filetypes=[("Crypton Key File", ".cey")], defaultextension=".cey") if file: with open(file, 'w', encoding="utf-8") as fob: fob.write(text) @staticmethod def BreakKey(key): every = 31 breaked = '\n'.join(key[i:i+every] for i in range(0, len(key), every)) return breaked @staticmethod def ReadCryptonKeyFile(): file = askopenfilename( filetypes=[("Crypton Key File", ".cey")], defaultextension=".cey") if file: with open(file, 'r', encoding="utf-8") as infile: return Crypton.ReadCryptonKeyString(infile.read()) @staticmethod def ReadCryptonKeyString(string): key = ''.join( string.replace("------ Crypton Key Begin ------\n", "")\ .replace("\n------ Crypton Key End ------", "")\ .split('\n') ) return key
关键修正点说明
- 所有二进制数据(密钥、ciphertext、tag、nonce)都通过Base64在字符串和字节之间转换,不再直接用UTF-8解码/编码。
AES.new()方法要求传入字节类型的密钥,因此加密解密时必须将Base64字符串转回字节。- 文件读写时指定
encoding="utf-8",确保和C#的文本编码一致。
内容的提问来源于stack exchange,提问作者DazzRick
相关产品推荐
相关产品推荐

