簡體   English   中英

如何使用 BouncyCastle Blowfish 實現解密數據?

[英]How to decrypt data using the BouncyCastle Blowfish implementation?

我正在使用一個外部 API 它返回給我一個 Blowfish 加密的 JSON 數組。 首先,我嘗試根據這篇文章c# Bouncy Castle Blowfish Decryption - Pad block corrupted使用 BountyCastle package 實現 Blowfish 加密/解密方法。

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

我收到了一個解密密鑰和這個 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:“墊塊損壞”

例外。 有人知道如何解密這個 Api 響應嗎?

PHP 代碼首先將三部分分開。 第二部分是 IV,它是十六進制解碼的,因此大小為 8 個字節。 第三部分是數據,由於第一部分中的 -1,它首先被十六進制解碼,然后通過在 CBC 模式下應用 Blowfish 和 PKCS7 填充,使用密鑰和 IV 解密。

要檢查 C# 實現,測試數據很有用:

  • 作為 20 字節密鑰,應用以下 Base64 解碼密鑰:
  MDEyMzQ1Njc4OTAxMjM0NTY3ODk=
  • 由於使用密文:
  $-1$cb8ba9e30b19ff2a$1cb8430f6b9c109e4334874408dbd26be36b0c9600383d63afd70669efcec38bea1290dfbe6b71519b1f48b514957845

如果您在 PHP 代碼中使用此數據, decryptData()將返回:

The quick brown fox jumps over the lazy dog

必須在 C# 代碼中進行以下更改:

  • 對 IV 的考慮
  • 使用 CBC 模式和 PKCS7 填充
  • 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
}

如果執行此代碼,則測試明文結果。 因此,使用此代碼,您的密文應該可以使用您的密鑰解密。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM