如何在C#的BouncyCastle中兼容非8字节块的Blowfish加密?
兼容Delphi MyDAC/LockBox3 Blowfish无填充加密的C#实现方案
问题核心分析
MyDAC和LockBox3的Blowfish无填充加密实现是非标准的:标准块密码(Blowfish块大小固定为8字节)的NoPadding要求输入长度必须是块大小的整数倍,但这两个Delphi库允许对不足块长度的数据直接加密(如单个字符转单个字节密文),而BouncyCastle的标准实现会因长度不匹配抛出异常。
1. 先确保密钥生成逻辑一致
Delphi的密钥生成逻辑需要在C#中精准复现,这是兼容的前提:
using System.Security.Cryptography; using System.Text; byte[] GenerateCompatibleKey(string password) { // 对应Delphi WideString的UTF-16LE编码 byte[] passBytes = Encoding.Unicode.GetBytes(password); // 计算SHA1哈希(20字节) byte[] sha1Hash; using (SHA1 sha1 = SHA1.Create()) { sha1Hash = sha1.ComputeHash(passBytes); } // 计算MD5哈希(16字节) byte[] md5Hash; using (MD5 md5 = MD5.Create()) { md5Hash = md5.ComputeHash(passBytes); } // 拼接成36字节数组,取前32字节作为最终密钥(对应MyDAC SetKey的32长度参数) byte[] keyBytes = new byte[36]; Buffer.BlockCopy(sha1Hash, 0, keyBytes, 0, 20); Buffer.BlockCopy(md5Hash, 0, keyBytes, 20, 16); byte[] finalKey = new byte[32]; Buffer.BlockCopy(keyBytes, 0, finalKey, 0, 32); return finalKey; }
2. 调整BouncyCastle加密逻辑兼容短数据
模拟Delphi库的非标准无填充行为:对不足块长度的数据,先补零到块大小,加密后截断回原数据长度;块倍数长度数据直接用标准CBC无填充加密。
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Security; byte[] EncryptWithBouncyCastle(string data, byte[] key) { byte[] plaintext = Encoding.UTF8.GetBytes(data); int blockSize = 8; // Blowfish块大小固定8字节 byte[] iv = new byte[8]; // 对应Delphi的全零IV if (plaintext.Length % blockSize == 0) { // 块倍数长度,直接用标准CBC无填充加密 ICipher cipher = CipherUtilities.GetCipher("Blowfish/CBC/NoPadding"); cipher.Init(true, new ParametersWithIV(new KeyParameter(key), iv)); return cipher.DoFinal(plaintext); } else { // 短数据:补零到块大小,加密后截断回原长度 byte[] paddedPlaintext = new byte[((plaintext.Length + blockSize - 1) / blockSize) * blockSize]; Buffer.BlockCopy(plaintext, 0, paddedPlaintext, 0, plaintext.Length); ICipher cipher = CipherUtilities.GetCipher("Blowfish/CBC/NoPadding"); cipher.Init(true, new ParametersWithIV(new KeyParameter(key), iv)); byte[] fullEncrypted = cipher.DoFinal(paddedPlaintext); // 截断到原明文长度 byte[] result = new byte[plaintext.Length]; Buffer.BlockCopy(fullEncrypted, 0, result, 0, plaintext.Length); return result; } } // 解密逻辑对应调整 byte[] DecryptWithBouncyCastle(byte[] encryptedData, byte[] key) { int blockSize = 8; byte[] iv = new byte[8]; if (encryptedData.Length % blockSize == 0) { ICipher cipher = CipherUtilities.GetCipher("Blowfish/CBC/NoPadding"); cipher.Init(false, new ParametersWithIV(new KeyParameter(key), iv)); return cipher.DoFinal(encryptedData); } else { // 补零到块大小,解密后截断回原密文长度 byte[] paddedEncrypted = new byte[((encryptedData.Length + blockSize - 1) / blockSize) * blockSize]; Buffer.BlockCopy(encryptedData, 0, paddedEncrypted, 0, encryptedData.Length); ICipher cipher = CipherUtilities.GetCipher("Blowfish/CBC/NoPadding"); cipher.Init(false, new ParametersWithIV(new KeyParameter(key), iv)); byte[] fullDecrypted = cipher.DoFinal(paddedEncrypted); byte[] result = new byte[encryptedData.Length]; Buffer.BlockCopy(fullDecrypted, 0, result, 0, encryptedData.Length); return result; } }
3. 可尝试的其他C#加密库
如果调整BouncyCastle逻辑后仍不兼容,可尝试以下库:
- .NET自定义Blowfish实现:基于
System.Security.Cryptography手动实现CBC模式的非标准块处理逻辑 - Cryptography.Cipher:轻量第三方库,支持自定义填充和块处理,适配非标准加密场景
- SharpZipLib:以压缩功能为主,但内置Blowfish实现,可调用其底层加密接口
- DotNetCrypto:全面的加密库,支持多种算法的灵活配置,适合兼容旧系统加密逻辑
内容的提问来源于stack exchange,提问作者John John
相关产品推荐
相关产品推荐

