自定义16字节块DES解密后文本乱码,如何修复?
修复自定义Feistel DES加密解密乱码问题
你的核心问题是解密逻辑错误调用了encrypt方法,Feistel结构的解密需要逆序使用子密钥并反转轮次流程,同时存在CBC模式IV长度不匹配、PKCS5去填充不严谨的问题。以下是完整修复方案:
修复后的完整代码
import pyDes class CustomDES: def __init__(self, key, iv): # 192位密钥分3个8字节子密钥 if len(key) != 24: raise ValueError("密钥必须为24字节(192位)") self.k1 = key[:8] self.k2 = key[8:16] self.k3 = key[16:] # CBC模式IV必须与自定义块大小一致(16字节) if len(iv) != 16: raise ValueError("IV必须为16字节") self.iv = iv def _f_function(self, data, subkey): # F函数复用标准DES的核心变换(ECB模式,无填充) des = pyDes.des(subkey, pyDes.ECB, pad=None, padmode=None) return des.encrypt(data) def _pkcs5_pad(self, data): block_size = 16 pad_len = block_size - len(data) % block_size return data + bytes([pad_len]) * pad_len def _pkcs5_unpad(self, data): if not data: return b"" pad_len = data[-1] # 验证填充合法性,避免乱码 if pad_len < 1 or pad_len > 16: raise ValueError("无效的PKCS5填充") if data[-pad_len:] != bytes([pad_len]) * pad_len: raise ValueError("PKCS5填充不匹配") return data[:-pad_len] def encrypt(self, plaintext): plain_bytes = plaintext.encode('utf-8') padded_data = self._pkcs5_pad(plain_bytes) ciphertext = b"" prev_block = self.iv # 按16字节块处理 for i in range(0, len(padded_data), 16): block = padded_data[i:i+16] # CBC模式:明文块异或上一个密文块(初始为IV) block_xor = bytes([b ^ pb for b, pb in zip(block, prev_block)]) # 3轮Feistel加密 l0, r0 = block_xor[:8], block_xor[8:] r1 = bytes([l ^ f for l, f in zip(l0, self._f_function(r0, self.k1))]) l1 = r0 r2 = bytes([l ^ f for l, f in zip(l1, self._f_function(r1, self.k2))]) l2 = r1 r3 = bytes([l ^ f for l, f in zip(l2, self._f_function(r2, self.k3))]) l3 = r2 cipher_block = l3 + r3 ciphertext += cipher_block prev_block = cipher_block return ciphertext def decrypt(self, ciphertext): plaintext = b"" prev_block = self.iv # 按16字节块处理 for i in range(0, len(ciphertext), 16): cipher_block = ciphertext[i:i+16] # 3轮Feistel解密:逆序使用子密钥K3→K2→K1 l3, r3 = cipher_block[:8], cipher_block[8:] # 轮3逆操作 r2 = l3 l2 = bytes([r ^ f for r, f in zip(r3, self._f_function(r2, self.k3))]) # 轮2逆操作 r1 = l2 l1 = bytes([r ^ f for r, f in zip(r2, self._f_function(r1, self.k2))]) # 轮1逆操作 r0 = l1 l0 = bytes([r ^ f for r, f in zip(r1, self._f_function(r0, self.k1))]) # 得到解密后的原始块 decrypted_block = l0 + r0 # CBC模式:解密块异或上一个密文块(初始为IV) plain_block = bytes([b ^ pb for b, pb in zip(decrypted_block, prev_block)]) plaintext += plain_block prev_block = cipher_block # 去填充并转明文 unpadded_data = self._pkcs5_unpad(plaintext) return unpadded_data.decode('utf-8') # 测试代码 if __name__ == "__main__": key = b"abcdefghijklmnopqrstuvwx" # 24字节密钥 iv = b"1234567890abcdef" # 16字节IV custom_des = CustomDES(key, iv) plaintext = "这是一段测试明文,Custom DES加密解密验证" cipher = custom_des.encrypt(plaintext) print("加密密文(十六进制):", cipher.hex()) decrypted_text = custom_des.decrypt(cipher) print("解密明文:", decrypted_text)
关键修复点
- 解密逻辑重构
- 完全重写
decrypt方法,不再复用encrypt逻辑。 - Feistel解密时逆序使用子密钥(K3→K2→K1),每轮操作与加密反向:加密是
R(n) = L(n-1) XOR F(R(n-1), K(n)),解密则是L(n-1) = R(n) XOR F(L(n), K(n))。
- 完全重写
- CBC模式修正
- IV长度改为16字节(与自定义块大小一致),块间异或操作覆盖完整16字节块,符合CBC模式规范。
- PKCS5去填充严谨性
- 添加填充合法性校验:检查最后一个字节的数值范围(1-16),且所有填充字节值等于填充长度,避免非法数据导致的乱码。
- 编码一致性
- 加密时明文统一转UTF-8字节,解密后统一从字节转UTF-8字符串,避免编码不匹配问题。
测试验证
运行测试代码后,会输出加密后的十六进制密文,以及完全还原的原始明文,解决之前的乱码问题。
内容的提问来源于stack exchange,提问作者wajde
相关产品推荐
相关产品推荐

