如何使用IV

时间:2015-08-12 13:20:12

标签: c# c#-4.0 encryption cryptography aes

似乎存在一种惯例,其中IV可以在AES加密文件的开头以明文存储。

在加密和解密期间提供密钥和IV时,我可以成功加密和解密文件。这使用下面的代码,稍作修改。如果该代码有帮助,请LMK,我将添加它。

但是,如果我尝试以明文将IV写入文件的开头,我无法解密内容(不确定它是否真的被加密了)。

有人可以指出下面的错误吗?不确定为什么.NET框架没有这个内置选项。

如果有人能指出我没有遵循上述惯例的方式,请指出它们!

using System;
using System.Security.Cryptography;

public class AESBase : IDisposable
{
    protected AesManaged AES;
    protected ICryptoTransform CryptoTransform;

    public AESBase(byte[] key, byte[] iv = null)
    {
        AES = new AesManaged
        {
            BlockSize = 128,
            KeySize = 256,
            Mode = CipherMode.CBC,
            Padding = PaddingMode.PKCS7,
            Key = key
        };

        if (iv != null) { AES.IV = iv; }
    }
}

using System.IO;
using System.Security.Cryptography;
using System.Threading.Tasks;

internal class AESFiles : AESBase, IFileCrytpo
{
    internal AESFiles(byte[] key, byte[] iv) : base(key, iv) { }

    #region internal methods

    public void Encrypt(string inputFileName, string outputFileName, bool overwriteFile)
    {
        CryptoTransform = AES.CreateEncryptor(AES.Key, AES.IV);

        if (overwriteFile)
        {
            DeleteFile(outputFileName);
        }

        Transform(inputFileName, outputFileName, true);
    }

    public void Decrypt(string inputFileName, string outputFileName, bool overwriteFile)
    {
        CryptoTransform = AES.CreateDecryptor(AES.Key, AES.IV);

        if (overwriteFile)
        {
            DeleteFile(outputFileName);
        }

        Transform(inputFileName, outputFileName, false);
    }

    #endregion public methods

    #region private methods

    private void Transform(string inputFileName, string outputFileName, bool encrypt)
    {
        var destination = new FileStream(outputFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None);
        if (encrypt)
        {
            //put the IV unencrypted in the front of the string
            destination.Write(AES.IV, 0, AES.BlockSize / 8);
        }

        var source = new FileStream(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read);
        if (!encrypt)
        {
            source.Read(AES.IV, 0, AES.BlockSize / 8);
            //var temp = Encoding.UTF8.GetString(AES.IV);
        }

        Transform(source, destination, CryptoTransform);
    }

    private static void Transform(Stream inputStream, Stream outputStream, ICryptoTransform transform)
    {
        using (var cryptoStream = new CryptoStream(outputStream, transform, CryptoStreamMode.Write))
        {
            //inputStream.Position = AES.BlockSize/8 + 1; CryptographicException : Length of the data to decrypt is invalid.
            //inputStream.Position = AES.BlockSize/8; CryptographicException : Padding is invalid and cannot be removed.

            inputStream.CopyTo(cryptoStream);
            cryptoStream.FlushFinalBlock();
        }
    }

    private static void DeleteFile(string fileName)
    {
        if (File.Exists(fileName))
        {
            File.Delete(fileName);
        }
    }

    #endregion private methods
}

[TestFixture]
class AESFilesTest
{
    private const string Path = @"C:\Users\Joe\Desktop\";
    private const string FileInput = "Input.csv";
    private const string FileEncrypted = "Encrypted.csv"; 
    private const string FileDecrypted = "Decrypted.csv";

    private readonly string _fileContents = String.Format("Test3,Test4" + Environment.NewLine, "Test5,Test6");

    private readonly byte[] _key;
    private readonly byte[] _iv;

    private readonly Engine _engine;

    public AESFilesTest()
    {
        _engine = new Engine();
        _key = Encoding.UTF8.GetBytes("CEC520FA51EA0A47E87295FA32442605"); //test key
        _iv = Encoding.UTF8.GetBytes("FB423A0BCB2AF4A4"); //test iv

        File.WriteAllText(Path + FileInput, _fileContents);
    }

    [Test]
    public void decrypted_text_matches_original()
    {
        const string inputFileWithPath = Path + FileInput;

        _engine.Encrypt(_key, _iv, inputFileWithPath, Path + FileEncrypted, true);
        _engine.Decrypt(_key, Path + FileEncrypted, Path + FileDecrypted, true);

        var decrypted = File.ReadAllText(Path + FileDecrypted);
        Console.WriteLine(decrypted);

        Assert.AreEqual(_fileContents, decrypted);
    }        

1 个答案:

答案 0 :(得分:3)

有很多错误......正确的代码:

private void Transform(string inputFileName, string outputFileName, bool encrypt)
{
    using (var source = new FileStream(inputFileName, FileMode.Open, FileAccess.Read, FileShare.Read))
    using (var destination = new FileStream(outputFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None))
    {
        ICryptoTransform cryptoTransform;

        if (encrypt)
        {
            //put the IV unencrypted in the front of the string
            destination.Write(AES.IV, 0, AES.BlockSize / 8);
            cryptoTransform = AES.CreateEncryptor(AES.Key, AES.IV);
        }
        else
        {
            byte[] bytes = new byte[AES.BlockSize / 8];
            source.Read(bytes, 0, bytes.Length);
            AES.IV = bytes;
            cryptoTransform = AES.CreateDecryptor(AES.Key, AES.IV);
        }

        Transform(source, destination, cryptoTransform, encrypt);
    }
}

private static void Transform(Stream inputStream, Stream outputStream, ICryptoTransform transform, bool encrypt)
{
    using (var cryptoStream = new CryptoStream(encrypt ? outputStream : inputStream, transform, encrypt ? CryptoStreamMode.Write : CryptoStreamMode.Read))
    {
        //inputStream.Position = AES.BlockSize/8 + 1; CryptographicException : Length of the data to decrypt is invalid.
        //inputStream.Position = AES.BlockSize/8; CryptographicException : Padding is invalid and cannot be removed.

        if (encrypt)
        {
            inputStream.CopyTo(cryptoStream);
            // Not needed. Done by the Dispose()
            //cryptoStream.FlushFinalBlock();
        }
        else
        {
            cryptoStream.CopyTo(outputStream);
        }
    }
}

然后删除现在没用的

protected ICryptoTransform CryptoTransform;

一般来说,加密和解密操作的处理完全不同......在很多地方参数都会发生变化。

另一个问题是阅读IV:您无法直接读取IV的{​​{1}}属性,您必须读取临时缓冲区({ {1}})然后将缓冲区分配给AesManaged

第三个问题:当您拥有bytesIV并且知道是否要加密或解密时,您必须创建CryptoTransform

啊......并注意到你没有写纯文本 IV,因为IV不是文本,它是二进制文件。您正在以二进制格式(或以其原生格式......或任何您想要的方式)编写IV。除非使用明文,否则表示未加密的IV。然后是的,你正在编写它的明文版本。但仍然无法使用Key对其进行编码,因为它不是" text" ...它是二进制数据。