c# - 如何使用 BouncyCaSTLe Blowfish 实现解密数据?

标签 c# encryption bouncycastle blowfish

我正在使用一个外部 API,它返回一个 Blowfish 加密的 JSON 数组。首先,我尝试使用基于这篇文章的 BountyCaSTLe 包来实现 Blowfish 加密/解密方法c# Bouncy Castle Blowfish Decryption - Pad block corrupted .

internal class Program
{
    private static void Main(string[] args)
    {
        string key = "KgKnVRujrgAv4XjD4bKCqdQVN5De0DCw8zpu1URnPw8="; // random
        string content = "[{'id':1},{'id':2}]";

        string encryptedContent = Encrypt(content, key);
        string decryptedContent = Decrypt(encryptedContent, key);

        /*
         
            decryptedContent returns 
        
                [{'id':1},{'id':2}]\0\0\0\0\0

            so I think this should be fine
         
         */
    }

    private static string Encrypt(string content, string encryptionKey)
    {
        byte[] contentBytes = Encoding.UTF8.GetBytes(content);

        return SharedCode(
            contentBytes,
            encryptionKey,
            true,
            encryptedContentBytes => BitConverter
                .ToString(encryptedContentBytes)
                .Replace("-", ""));
    }

    private static string Decrypt(string encryptedContent, string decryptionKey)
    {
        byte[] contentBytes = Hex.Decode(encryptedContent);

        return SharedCode(contentBytes, decryptionKey, false, decryptedContentBytes =>
        {
            string decryptedContentString = BitConverter
                .ToString(decryptedContentBytes)
                .Replace("-", "");

            byte[] hexBytes = new byte[decryptedContentString.Length / 2];
            
            for (int i = 0; i < hexBytes.Length; i++)
            {
                string currentHexString = decryptedContentString.Substring(i * 2, 2);
                hexBytes[i] = Convert.ToByte(currentHexString, 16);
            }

            return Encoding.UTF8.GetString(hexBytes);
        });
    }

    private static string SharedCode(byte[] contentBytes, string key, bool forceEncryption, Func<byte[], string> processor)
    {
        BlowfishEngine blowfishEngine = new BlowfishEngine();
        PaddedBufferedBlockCipher paddedBufferedBlockCipher = new PaddedBufferedBlockCipher(blowfishEngine);
        byte[] keyBytes = Encoding.UTF8.GetBytes(key);
        KeyParameter keyParameter = new KeyParameter(keyBytes);
        paddedBufferedBlockCipher.Init(forceEncryption, keyParameter);
        int outputLength = paddedBufferedBlockCipher.GetOutputSize(contentBytes.Length);
        byte[] outputBytes = new byte[outputLength];
        int processedBytes = paddedBufferedBlockCipher.ProcessBytes(contentBytes, 0, contentBytes.Length, outputBytes, 0);
        paddedBufferedBlockCipher.DoFinal(outputBytes, processedBytes);

        return processor(outputBytes);
    }
}
现在我想解密 API 响应。 Api 返回给我以下 Blowfish 加密的 JSON 正文内容

$-1$cb8ba9e30b19ff2a$d1157421764fe503d1fa9810fb9e6c3b424a1e8d014a321f5a2fb47ec6ebc8287d4d6236448d3623be42cf927fb883ca48810037c1a62bd229f937727c272c76420eb1f630bb2856c27d10c955220a1539f64e07c5708db90787ac470cad8372ea086501981c7a53ca69740c7ccfced856e234a6801efcf1f71178e75646441ba2716ea75a75ff3e6e002ba08ad18efeef95a909c9a5c68087cc63ed138a63c6788b9bbc43f3c04d2a496660f84ac98f011d3930c61ce9d5565131d2cba65db7c9bef824dd9a6594


我收到了一个解密 key 和这个 PHP 代码示例作为“文档”。响应本身包含三组
  • switch 语句的字符串(例如 -1)
  • 代表盐的十六进制字符串
  • 表示内容的十六进制字符串

  • .
    <?php
    function decryptData(string $data, string $key): string{
        $matches = [];
        if(!preg_match('|^\$([^$]{2,4})\$([a-f0-9]{16,64})\$|i', $data, $matches)){
            return '';
        }
        $data = (string) substr($data, strlen($matches[0]));
        switch($matches[1]){
            default:
                return '';
            case '-1':
                $data = (string) hex2bin($data);
            case '-1a':
                $algo = 'blowfish';
                return (string) openssl_decrypt($data, $algo, (string) base64_decode($key), OPENSSL_RAW_DATA, (string) hex2bin($matches[2]));
        }
    }
    
    我个人不知道盐的用途是什么,因为我的实现不需要它,但我尝试将代码更新为此
    internal class Program
    {
        private static void Main(string[] args)
        {
            string key = "KgKnVRujrgAv4XjD4bKCqdQVN5De0DCw8zpu1URnPw8"; // this decryption key is not the correct one to use
            string apiResponse = "$-1$cb8ba9e30b19ff2a$d1157421764fe503d1fa9810fb9e6c3b424a1e8d014a321f5a2fb47ec6ebc8287d4d6236448d3623be42cf927fb883ca48810037c1a62bd229f937727c272c76420eb1f630bb2856c27d10c955220a1539f64e07c5708db90787ac470cad8372ea086501981c7a53ca69740c7ccfced856e234a6801efcf1f71178e75646441ba2716ea75a75ff3e6e002ba08ad18efeef95a909c9a5c68087cc63ed138a63c6788b9bbc43f3c04d2a496660f84ac98f011d3930c61ce9d5565131d2cba65db7c9bef824dd9a6594";
    
            Match matches = Regex.Match(apiResponse, @"^\$([^$]{2,4})\$([a-f0-9]{16,64})\$([a-f0-9]*)");
            Group algorithm = matches.Groups[1];
            Group salt = matches.Groups[2];
            Group content = matches.Groups[3];
            string encryptedContent = content.ToString();
            string decryptedContent = Decrypt(encryptedContent, key);
        }
    
        private static string Decrypt(string encryptedContent, string decryptionKey)
        {
            byte[] contentBytes = Hex.Decode(encryptedContent);
            BlowfishEngine blowfishEngine = new BlowfishEngine();
            PaddedBufferedBlockCipher paddedBufferedBlockCipher = new PaddedBufferedBlockCipher(blowfishEngine);
            byte[] keyBytes = Encoding.UTF8.GetBytes(decryptionKey);
            KeyParameter keyParameter = new KeyParameter(keyBytes);
            paddedBufferedBlockCipher.Init(false, keyParameter);
            int outputLength = paddedBufferedBlockCipher.GetOutputSize(contentBytes.Length);
            byte[] outputBytes = new byte[outputLength];
            int processedBytes = paddedBufferedBlockCipher.ProcessBytes(contentBytes, 0, contentBytes.Length, outputBytes, 0);
            paddedBufferedBlockCipher.DoFinal(outputBytes, processedBytes); // throws Org.BouncyCastle.Crypto.InvalidCipherTextException: 'pad block corrupted'
    
            string decryptedContentString = BitConverter
                .ToString(outputBytes)
                .Replace("-", "");
    
            byte[] hexBytes = new byte[decryptedContentString.Length / 2];
    
            for (int i = 0; i < hexBytes.Length; i++)
            {
                string currentHexString = decryptedContentString.Substring(i * 2, 2);
                hexBytes[i] = Convert.ToByte(currentHexString, 16);
            }
    
            return Encoding.UTF8.GetString(hexBytes);
        }
    }
    
    不幸的是,代码在
    paddedBufferedBlockCipher.DoFinal(outputBytes, processedBytes);
    
    抛出一个

    Org.BouncyCastle.Crypto.InvalidCipherTextException: 'pad block corrupted'


    异常(exception)。有人知道如何解密这个 Api 响应吗?

    最佳答案

    PHP 代码首先将三部分分开。第二部分是 IV,它是十六进制解码的,因此大小为 8 个字节。第三部分是数据,由于第一部分中的 -1,首先对其进行十六进制解码,然后通过在 CBC 模式下应用 Blowfish 并使用 PKCS7 填充使用 key 和 IV 进行解密。
    要检查 C# 实现,测试数据很有用:

  • 作为 20 字节的 key ,应用了以下 Base64 解码 key :

  •   MDEyMzQ1Njc4OTAxMjM0NTY3ODk=
    
  • 使用密文:

  •   $-1$cb8ba9e30b19ff2a$1cb8430f6b9c109e4334874408dbd26be36b0c9600383d63afd70669efcec38bea1290dfbe6b71519b1f48b514957845
    
    如果您在 PHP 代码中使用此数据,decryptData()返回:
    The quick brown fox jumps over the lazy dog
    
    必须在 C# 代码中进行以下更改:
  • 考虑IV
  • 使用 CBC 模式和 PKCS7 填充
  • key 的 Base64 解码

  • 这导致例如在下面的实现中:
    private static void Main(string[] args)
    {
        //string key = "KgKnVRujrgAv4XjD4bKCqdQVN5De0DCw8zpu1URnPw8"; // this decryption key is not the correct one to use
        //string apiResponse = "$-1$cb8ba9e30b19ff2a$d1157421764fe503d1fa9810fb9e6c3b424a1e8d014a321f5a2fb47ec6ebc8287d4d6236448d3623be42cf927fb883ca48810037c1a62bd229f937727c272c76420eb1f630bb2856c27d10c955220a1539f64e07c5708db90787ac470cad8372ea086501981c7a53ca69740c7ccfced856e234a6801efcf1f71178e75646441ba2716ea75a75ff3e6e002ba08ad18efeef95a909c9a5c68087cc63ed138a63c6788b9bbc43f3c04d2a496660f84ac98f011d3930c61ce9d5565131d2cba65db7c9bef824dd9a6594";
    
        string key = "MDEyMzQ1Njc4OTAxMjM0NTY3ODk=";
        string apiResponse = "$-1$cb8ba9e30b19ff2a$1cb8430f6b9c109e4334874408dbd26be36b0c9600383d63afd70669efcec38bea1290dfbe6b71519b1f48b514957845";
    
        Match matches = Regex.Match(apiResponse, @"^\$([^$]{2,4})\$([a-f0-9]{16,64})\$([a-f0-9]*)");
        Group algorithm = matches.Groups[1];
        Group iv = matches.Groups[2];                                                               // 2nd part is the IV
        Group content = matches.Groups[3];      
    
        string decryptedContent = Decrypt(content.ToString(), key, iv.ToString());                  // Pass additionally the IV
        Console.WriteLine(decryptedContent);
    }
    
    private static string Decrypt(string encryptedContent, string decryptionKey, string iv)
    {
        byte[] contentBytes = Hex.Decode(encryptedContent);
    
        byte[] keyBytes = Convert.FromBase64String(decryptionKey);                                  // Base64 decode the key
        KeyParameter keyParameter = new KeyParameter(keyBytes);
        ParametersWithIV keyParamWithIv = new ParametersWithIV(keyParameter, Hex.Decode(iv));       // Consider the IV
    
        BlowfishEngine blowfishEngine = new BlowfishEngine(); 
        PaddedBufferedBlockCipher paddedBufferedBlockCipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(blowfishEngine), new Pkcs7Padding()); // Consider CBC mode and PKCS7 padding
        paddedBufferedBlockCipher.Init(false, keyParamWithIv);
        int outputLength = paddedBufferedBlockCipher.GetOutputSize(contentBytes.Length);
        byte[] outputBytes = new byte[outputLength];
        int processedBytes = paddedBufferedBlockCipher.ProcessBytes(contentBytes, 0, contentBytes.Length, outputBytes, 0);
        processedBytes += paddedBufferedBlockCipher.DoFinal(outputBytes, processedBytes); 
        
        return Encoding.UTF8.GetString(outputBytes, 0, processedBytes); // The quick brown fox jumps over the lazy dog
    }
    
    如果执行此代码,则测试明文结果。因此,使用此代码,您的密文应该可以使用您的 key 解密。

    关于c# - 如何使用 BouncyCaSTLe Blowfish 实现解密数据?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66370462/

    相关文章:

    security - 从 VBA (MS Access) 解密 PGP 加密文件的最简单方法

    java - 解密带有特殊字符的字符串时出现ArrayIndexOutOfBoundsException

    c# - Task.WhenAny - 剩余的运行任务会怎样?

    java - 为什么我在解密时得到 'BadPaddingException'?

    c# - MySql.Data 与 MySql.Data.CF

    java - 我的字符切换软件返回相同的答案?

    java - 在JAVA中使用AES/GCM检测不正确的 key

    c# - Blowfish 引擎的 Bouncy CaSTLe CTS 模式未按预期工作

    c# - 在一个简单的 C# metro 应用程序中崩溃,错误代码为 0x5d039293

    c# - 使用 ViewModel 方法的命令绑定(bind)