0

我正在尝试使用 bouncycastle 库加密和解密数据,在解密数据后,数据的第一个信息块已损坏,就好像它未能成功解密并且最后一个块完全丢失一样。我是 bouncycastle 库的新手,我一直在互联网上搜索,试图使用 PKCS7Padding 在 CBC 模式下找到 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'·ðúœ×ñð ersion Releases

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

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

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

4

3 回答 3

3

这段代码有几个问题,但导致您的解密问题的一个是您对 IV 的使用。您在加密期间生成一个随机 IV(很好),但随后您将其丢弃。然后,您在解密期间生成不同的随机 IV,这是不正确的。您需要使用与加密相同的 IV 进行解密。您通常将 IV 与密文一起传递(通常在密文之前是最简单的)。

您的密钥生成也不正确。据我所知,您期望使用 Base-64 编码的“密码短语”。然后你在密钥长度处将其切断,或者用 0 填充它。这是非常不安全的。您基本上是将 AES-256 转换为 AES-50 左右。它看起来是加密的,但实际上它有一个很小的密钥空间并且可以被暴力破解。

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

于 2012-10-04T19:33:41.827 回答
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。

于 2012-10-09T19:13:37.813 回答
1

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

于 2012-10-05T08:33:55.480 回答