.NET AES/Rijndael — 重用解密器时解密不一致

3
我已经创建了一个使用AES进行加密和解密的类。
public class AesEncryptionProvider {
    #region Fields

    // Encryption key
    private static readonly byte[] s_key = new byte[32] {
        // Omitted...
    };

    // Initialization vector
    private static readonly byte[] s_iv = new byte[16] {
        // Omitted...
    };

    private AesCryptoServiceProvider m_provider;
    private ICryptoTransform m_encryptor;
    private ICryptoTransform m_decryptor;

    #endregion

    #region Constructors

    private AesEncryptionProvider () {
        m_provider = new AesCryptoServiceProvider();
        m_encryptor = m_provider.CreateEncryptor(s_key, s_iv);
        m_decryptor = m_provider.CreateDecryptor(s_key, s_iv);
    }

    static AesEncryptionProvider () {
        Instance = new AesEncryptionProvider();
    }

    #endregion

    #region Properties

    public static AesEncryptionProvider Instance { get; private set; }

    #endregion

    #region Methods

    public string Encrypt (string value) {
        if (string.IsNullOrEmpty(value)) {
            throw new ArgumentException("Value required.");
        }

        return Convert.ToBase64String(
            Transform(
                Encoding.UTF8.GetBytes(value),
                m_encryptor));
    }

    public string Decrypt (string value) {
        if (string.IsNullOrEmpty(value)) {
            throw new ArgumentException("Value required.");
        }

        return Encoding.UTF8.GetString(
            Transform(
                Convert.FromBase64String(value),
                m_decryptor));
    }

    #endregion

    #region Private methods

    private byte[] Transform (byte[] input, ICryptoTransform transform) {
        byte[] output;
        using (MemoryStream memory = new MemoryStream()) {
            using (CryptoStream crypto = new CryptoStream(
                memory,
                transform,
                CryptoStreamMode.Write
            )) {
                crypto.Write(input, 0, input.Length);
                crypto.FlushFinalBlock();

                output = memory.ToArray();
            }
        }
        return output;
    }

    #endregion
}

正如您所看到的,在这两种情况下,我都是通过CryptoStream将数据writingMemoryStream中。如果我在每次调用Decrypt时通过m_provider.CreateDecyptor(s_key, s_iv)创建一个新的解密器,它就可以正常工作。

出了什么问题?为什么解密器表现得好像忘记了IV一样?调用StreamReader.ReadToEnd()是否有助于m_decryptor正确运行?

我希望避免我列出的这两种“可行”的方法,因为它们都会对性能产生影响,而这是非常关键的路径。提前致谢。


1
请发布实际的加密和解密代码。这种方法听起来并不会有问题。 - Jesse C. Slicer
1个回答

1

好的,我承认我不知道为什么这个可以工作,但是将 AesCryptoServiceProvider 更改为 AesManaged 就可以了。

我还建议您让您的类实现 IDisposable 接口,因为它包含三个实现了该接口的成员变量。请参见以下代码更改:

public sealed class AesEncryptionProvider : IDisposable
{
    // Encryption key
    private static readonly byte[] key = new byte[]
    {
        // Omitted...
    };

    // Initialization vector
    private static readonly byte[] iv = new byte[]
    {
        // Omitted...
    };

    private static readonly AesEncryptionProvider instance = new AesEncryptionProvider();

    private readonly AesManaged provider;

    private readonly ICryptoTransform encryptor;

    private readonly ICryptoTransform decryptor;

    private AesEncryptionProvider()
    {
        this.provider = new AesManaged();
        this.encryptor = this.provider.CreateEncryptor(key, iv);
        this.decryptor = this.provider.CreateDecryptor(key, iv);
    }

    public static AesEncryptionProvider Instance
    {
        get
        {
            return instance;
        }
    }

    public void Dispose()
    {
        this.decryptor.Dispose();
        this.encryptor.Dispose();
        this.provider.Dispose();
        GC.SuppressFinalize(this);
    }

    public string Encrypt(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            throw new ArgumentException("Value required.");
        }

        return Convert.ToBase64String(Transform(Encoding.UTF8.GetBytes(value), this.encryptor));
    }

    public string Decrypt(string value)
    {
        if (string.IsNullOrEmpty(value))
        {
            throw new ArgumentException("Value required.");
        }

        return Encoding.UTF8.GetString(Transform(Convert.FromBase64String(value), this.decryptor));
    }

    private static byte[] Transform(byte[] input, ICryptoTransform transform)
    {
        using (var memory = new MemoryStream())
        using (var crypto = new CryptoStream(memory, transform, CryptoStreamMode.Write))
        {
            crypto.Write(input, 0, input.Length);
            crypto.FlushFinalBlock();
            return memory.ToArray();
        }
    }
}

我正要发布同样的内容!它可以使用你所说的RijndaelManaged或AesManaged。我找到了一些关于这三者之间差异的信息 (https://dev59.com/9XM_5IYBdhLWcg3wymU0),我选择了AesManaged。这是一个相当神秘的问题,但至少现在它能够工作了!谢谢! - iamtyler
只使用托管加密算法的问题在于它们不符合FIPS标准。我也有同样的问题,但我需要我的实现符合FIPS标准。 - Darren
@iamtyler,您也可以重复使用AesCryptoServiceProvider实例,并为每个调用创建新的加密器和解密器。这对我很有效。 - Josef Bláha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接