使用BouncyCastle进行Rijndael解密生成数据的开头和结尾已损坏,但其余数据完全解密

时间:2012-10-04 19:03:24

标签: c#-4.0 cryptography aes bouncycastle

我正在尝试使用bouncycastle库加密和解密数据,在解密数据时,第一个信息块被破坏的数据好像无法成功解密并且最后一个块完全丢失。我是bouncycastle图书馆的新手,我一直在互联网上搜索,试图在CBC模式下使用PKCS7Padding找到一个合理的AES加密实现,但我还没有找到很多文档。我将非常感谢您提供的任何帮助,我也应该注意到我是一名学生而非专业开发人员。感谢。

`public class RijndaelCBC
{
    private int _keySize;
    private byte[] _passphrase;

    public byte[] _iv { get; private set; }

    private IBlockCipher blockCipher;
    private PaddedBufferedBlockCipher aesCipher;
    private ParametersWithIV _param;

    public RijndaelCBC(int KeySize, string Passphrase)
    {
        if (Passphrase.Length < KeySize / 8)
            Passphrase = Passphrase.PadRight(KeySize / 8, '0');
        if (Passphrase.Length > KeySize)
            Passphrase = Passphrase.Substring(0, KeySize / 8);

        _passphrase = System.Convert.FromBase64String(Passphrase);
        Array.Resize(ref _passphrase, KeySize / 8);
        _keySize = KeySize;

        Random rnd = new Random();
        _iv = new byte[_keySize / 8];
        for (int t = 0; t < _keySize / 8; t++)
            rnd.Next();
        rnd.NextBytes(_iv);

        if (_keySize != 128 && _keySize != 192 && _keySize != 256)
            throw new Exception(string.Format("Invalid key size of {0} provided, cannot continue with the process.", _keySize));
    }

    public RijndaelCBC(int KeySize, string Passphrase, byte[] iv)
    {
        if (Passphrase.Length < KeySize / 8)
            Passphrase = Passphrase.PadRight(KeySize / 8, '0');
        if (Passphrase.Length > KeySize)
            Passphrase = Passphrase.Substring(0, KeySize / 8);


        _passphrase = System.Convert.FromBase64String(Passphrase);
        Array.Resize(ref _passphrase, KeySize / 8);
        _keySize = KeySize;
        _iv = iv;
        if (_keySize != 128 && _keySize != 192 && _keySize != 256)
            throw new Exception(string.Format("Invalid key size of {0} provided, cannot continue with the process.", _keySize));
    }

    public byte[] Encrypt(byte[] data)
    {
        try
        {
            blockCipher = new CbcBlockCipher(new RijndaelEngine(_keySize));
            _param = new ParametersWithIV(new KeyParameter(_passphrase), _iv);
            blockCipher.Init(true, _param);

            aesCipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
            byte[] cipherTextBlock = null; 
            int blockSize = aesCipher.GetBlockSize();
            List<byte> output = new List<byte>();
            int outputLen = 0;
            int chunkPosition = 0;
            for (chunkPosition = 0; chunkPosition < data.Length; chunkPosition += blockSize)
            {
                byte[] dataToProcess = new byte[blockSize];
                int chunkSize = (data.Length - chunkPosition) < blockSize ? (data.Length - chunkPosition) : blockSize;
                Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);

                cipherTextBlock = new byte[blockSize];
                outputLen = aesCipher.ProcessBytes(dataToProcess, 0, chunkSize, cipherTextBlock, 0);
                output.AddRange(cipherTextBlock);
            }
            try
            {
                if(chunkPosition < data.Length &&
                    chunkPosition + blockSize > data.Length)
                {
                    byte[] dataToProcess = new byte[blockSize];
                    int chunkSize = (chunkPosition + blockSize) - data.Length;
                    Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);
                    aesCipher.DoFinal(cipherTextBlock, 0);
                    output.AddRange(cipherTextBlock);
                }
            }
            catch (CryptoException ex)
            {}
            return output.ToArray();
        }
        catch (System.Exception ex)
        { }
        return null;
    }

    public byte[] Decrypt(byte[] data)
    {
        try
        {
            blockCipher = new CbcBlockCipher(new RijndaelEngine(_keySize));
            _param = new ParametersWithIV(new KeyParameter(_passphrase), _iv);
            blockCipher.Init(false, _param);

            aesCipher = new PaddedBufferedBlockCipher(blockCipher, new Pkcs7Padding());
            byte[] cipherTextBlock = null;   
            int blockSize = aesCipher.GetBlockSize();
            List<byte> output = new List<byte>();
            int outputLen = 0;
            int chunkPosition = 0;
            for (chunkPosition = 0; chunkPosition < data.Length; chunkPosition += blockSize)
            {
                byte[] dataToProcess = new byte[blockSize];
                int chunkSize = (data.Length - chunkPosition) < blockSize ? (data.Length - chunkPosition) : blockSize;
                Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);
                cipherTextBlock = new byte[blockSize];

                outputLen = aesCipher.ProcessBytes(dataToProcess, 0, chunkSize, cipherTextBlock, 0);
                output.AddRange(cipherTextBlock);
            }
            try
            {
                if (chunkPosition < data.Length &&
                    chunkPosition + blockSize > data.Length)
                {
                    byte[] dataToProcess = new byte[blockSize];
                    int chunkSize = (chunkPosition + blockSize) - data.Length;
                    Buffer.BlockCopy(data, chunkPosition, dataToProcess, 0, chunkSize);
                    aesCipher.DoFinal(cipherTextBlock, 0);
                    output.AddRange(cipherTextBlock);
                }
            }
            catch (CryptoException ex)
            { }
            return output.ToArray();
        }
        catch (System.Exception ex)
        { }
        return null;
    }
}`

输出示例: 数据的开头应该是:

生产版本发布

但结束为: [¨dZJÊ)uól)ȱýº-ÑÚ~VE'·ðúœ×ñðersionReleases

和数据结尾应为: 对于此类产品,英特尔不承担任何责任, 英特尔不承担任何明示或暗示的保证, 有关销售和/或使用英特尔产品的信息,包括 与健身相关的责任或保证 特殊目的,适销性或侵权 专利,版权或其他知识产权。 英特尔产品不适用于医疗, 拯救生命或维持生命的应用。

但最终为: 对于此类产品,英特尔不承担任何责任, 英特尔不承担任何明示或暗示的保证, 有关销售和/或使用英特尔产品的信息,包括 责任或保证

我试图对一个例子进行加密和解密,但最后数据丢失,一开始数据没有正确解密,但52KB文件的其余部分是完美的。

3 个答案:

答案 0 :(得分:3)

此代码中存在多个问题,但导致解密问题的问题是您使用IV。你在加密期间生成一个随机的IV(好),但是你把它扔掉了。然后,您在解密期间生成不同的随机IV,这是不正确的。您需要使用相同的IV进行解密才能进行加密。您通常将密码与密文一起传递(通常在密文之前最简单)。

您的密钥生成也不正确。我可以说,你期待Base-64编码中的“密码短语”。然后,您可以按键长度将其切断,或用0填充它。这是非常不安全的。您基本上将AES-256转换为AES-50左右。它看起来是加密的,但它实际上有一个很小的键空间,可以强制使用。

将人类型密码转换为AES密钥的正确方法是使用名为PBKDF2的算法。我对bouncycastle并不是特别熟悉,也不知道他们为PBKDF2使用了什么提供商。有关更多特定于bouncycastle的详细信息,请参阅PBKDF2 with bouncycastle in Java

答案 1 :(得分:2)

我最终要做的是让代码工作是将填充块密码更改为缓冲块密码,不确定它们是否是这样做的任何负面影响但是我包含了代码以防万一有人需要它在C#

中很难找到bouncycastle的例子
public class RijndaelCBC
{
    private int _keyBitSize;
    public byte[] _iv { get; private set; }

    private BufferedBlockCipher cipher;
    private KeyParameter key;
    private ParametersWithIV IVkey;

    public RijndaelCBC(int KeySize, byte[] salt, string Passphrase)
    {
    _keyBitSize = KeySize;

        Random rnd = new Random();

        _iv = new byte[_keyBitSize / 8];
        for (int t = 0; t < _keyBitSize / 8; t++)
            rnd.Next();
        rnd.NextBytes(_iv);

        PbeParametersGenerator generator = new Pkcs5S2ParametersGenerator();
        generator.Init(PbeParametersGenerator.Pkcs5PasswordToUtf8Bytes((Passphrase).ToCharArray()), salt, 1000);
        key = (KeyParameter)generator.GenerateDerivedParameters("AES", _keyBitSize);
    }

    public RijndaelCBC(int KeySize, byte[] salt, string Passphrase, byte[] iv)
    {
        _iv = iv;

        PbeParametersGenerator generator = new Pkcs5S2ParametersGenerator();
        generator.Init(PbeParametersGenerator.Pkcs5PasswordToUtf8Bytes((Passphrase).ToCharArray()), salt, 1000);
        key = (KeyParameter)generator.GenerateDerivedParameters("AES", _keyBitSize);
    }


    public byte[] Encrypt(byte[] data)
    {
        IBlockCipher theCipher = null;
        theCipher = new RijndaelEngine(_keyBitSize);
        cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(theCipher));
        IVkey = new ParametersWithIV(key, _iv);
        cipher.Init(true, IVkey);

        int size = cipher.GetOutputSize(data.Length);
        byte[] result = new byte[size];
        int olen = cipher.ProcessBytes(data, 0, data.Length, result, 0);
        olen += cipher.DoFinal(result, olen);

        if (olen < size)
        {
            byte[] tmp = new byte[olen];
            Array.Copy(result, 0, tmp, 0, olen);
            result = tmp;
        }

        return result;
    }

    public byte[] Decrypt(byte[] data)
    {
        IBlockCipher theCipher = null;
        theCipher = new RijndaelEngine(_keyBitSize);
        cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(theCipher));
        IVkey = new ParametersWithIV(key, _iv);
        cipher.Init(false, IVkey);

        int size = cipher.GetOutputSize(data.Length);
        byte[] result = new byte[size];
        int olen = cipher.ProcessBytes(data, 0, data.Length, result, 0);
        olen += cipher.DoFinal(result, olen);

        if (olen < size)
        {
            byte[] tmp = new byte[olen];
            Array.Copy(result, 0, tmp, 0, olen);
            result = tmp;
        }

        return result;
    }
}

关于上面提到的类的初始化问题,第一个构造函数使用随机数据生成IV,可以用于加密,在生成所述IV之后,您可以通过访问_iv属性来获取IV。或者,您可以使用第二类构造函数在加密和/或解密期间传递IV。

答案 2 :(得分:1)

好吧,你没有正确获得第一个块的原因必须是IV值或缓冲区处理问题。最后一个可能比你预期的更合乎逻辑,因为你以错误的方式使用DoFinal结束了。您为它提供输入缓冲区而不是输出缓冲区。