简体   繁体   English

C# AES 和 RSA 文件加密 - 如何使用 IV?

[英]C# AES and RSA File Encryption - How to use IV?

I'm writing a program at the moment that works under the following scenario:我正在编写一个程序,它可以在以下情况下工作:

  • I've got some confidential log files that I need to backup to a server.我有一些需要备份到服务器的机密日志文件。
  • I have a program that generates these log files every day.我有一个程序每天都会生成这些日志文件。
  • These log files would rarely if ever need to be opened.这些日志文件很少需要打开。
  • I have only one RSA public/private key pair.我只有一个 RSA 公钥/私钥对。
  • The program has only the RSA public key.该程序只有 RSA 公钥。
  • I generate a random AES key each time the program makes one of these confidential files.每次程序制作这些机密文件之一时,我都会生成一个随机的 AES 密钥。
  • The program uses this AES key to encrypt the log file.该程序使用此 AES 密钥来加密日志文件。
  • I then use the RSA public key to encrypt the AES Key然后我使用 RSA 公钥来加密 AES 密钥
  • I then backup both the AES encrypted file and RSA encrypted AES key to the server.然后我将 AES 加密文件和 RSA 加密的 AES 密钥备份到服务器。

As far as I understand, that protocol is fitting for my use case.据我了解,该协议适合我的用例。

The issue I'm having is coding it up in C#.我遇到的问题是用 C# 编码。 I ran into needing an Initialization Vector(IV) for my AES encryption, I tried to encrypt this along with the AES key by using the public RSA key on both.我遇到了 AES 加密需要初始化向量 (IV) 的情况,我尝试通过在两者上使用公共 RSA 密钥来将其与 AES 密钥一起加密。 But the 512(2 * 256) size is larger than RSA is happy to encrypt.但是 512(2 * 256) 大小比 RSA 乐于加密的要大。 So I figured out since I created the Initialization Vector randomly each time just like the AES Key, I can add the IV to the front of the AES ciphertext.所以我想,既然我每次都像 AES 密钥一样随机创建初始化向量,我可以将 IV 添加到 AES 密文的前面。 However, I'm not sure where the code to do this would be inserted in my functions但是,我不确定在我的函数中执行此操作的代码的位置

Any help in the right direction to the "protocol" or other ways to write the IV to the ciphertext would be great.任何对“协议”或其他将 IV 写入密文的方法的正确方向的帮助都会很棒。 Thank you in advance.先感谢您。

static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
    {
        byte[] encryptedAES = null;
        byte[] encryptedRSA = null;

        using (MemoryStream ms = new MemoryStream())
        {
            using (RijndaelManaged AES = new RijndaelManaged())
            {
                AES.KeySize = 256;
                AES.BlockSize = 128;
                AES.Mode = CipherMode.CBC;
                AES.GenerateIV();
                AES.GenerateKey();
                encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);

                using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
                {
                    ms.Write(AES.IV, 0, AES.KeySize); //DOESNT WORK HERE
                    //Can't use CS to write it to the stream else it will encrypt along with file
                    cs.Write(toEncryptAES, 0, toEncryptAES.Length);
                    cs.Close();
                }
                encryptedAES = ms.ToArray();
            }
        }
        return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
    }
    static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
    {
        byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);

        using (MemoryStream ms = new MemoryStream())
        {
            using (RijndaelManaged AES = new RijndaelManaged())
            {
                AES.KeySize = 256;
                AES.BlockSize = 128;
                AES.Key = AESKey;
                ms.Read(AES.IV, 0, AES.KeySize); //Not sure if can read MS here
                AES.Mode = CipherMode.CBC;

                using (var cs = new CryptoStream(ms, AES.CreateDecryptor(), CryptoStreamMode.Write))
                {
                    //Would I need to move 0 to 256?
                    cs.Write(toDecryptAES, 0, toDecryptAES.Length);
                    cs.Close();
                }
                return ms.ToArray();
            }
        }
    }

You where quite close, write out the IV before you create the CryptoStream您非常接近,请在创建 CryptoStream 之前写出 IV

static public Tuple<byte[], byte[]> EncryptAES(byte[] toEncryptAES, RSAParameters RSAPublicKey)
{
    byte[] encryptedAES = null;
    byte[] encryptedRSA = null;

    using (MemoryStream ms = new MemoryStream())
    {
        using (RijndaelManaged AES = new RijndaelManaged())
        {
            AES.KeySize = 256;
            AES.BlockSize = 128;
            AES.Mode = CipherMode.CBC;
            AES.GenerateIV();
            AES.GenerateKey();
            encryptedRSA = RSAEncrypt(AES.Key, RSAPublicKey);

            ms.Write(AES.IV, 0, AES.KeySize); //Move the write here.

            using (var cs = new CryptoStream(ms, AES.CreateEncryptor(), CryptoStreamMode.Write))
            {
                cs.Write(toEncryptAES, 0, toEncryptAES.Length);
                cs.Close();
            }
            encryptedAES = ms.ToArray();
        }
    }
    return new Tuple<byte[], byte[]>(encryptedAES, encryptedRSA);
}

For the decrypt, make sure you loop over the read till you have fully read the byte[] for the IV, Stream.Read is not guaranteed to read all the bytes you asked it to read.对于解密,请确保循环读取直到您完全读取了 IV 的 byte[], Stream.Read不能保证读取您要求它读取的所有字节。 I usually make a static method ReadFully to ensure all bytes are read.我通常会创建一个静态方法ReadFully来确保读取所有字节。

private static byte[] ReadFully(Stream stream, int length)
{
    int offset = 0;
    byte[] buffer = new byte[length];
    while(offset < length)
    {
        offset += stream.Read(buffer, offset, length - offset);
    }
    return buffer;
}

Then just use that method to read in the IV.然后只需使用该方法读取IV。 You also want to use cs.Read not cs.Write to read out the encrypted data and put the stream in to read mode, however it is easier to just use .CopyTo and copy the data to a new MemoryStream.您还想使用cs.Read而不是cs.Write来读出加密数据并将流置于读取模式,但是使用.CopyTo并将数据复制到新的 MemoryStream 会更容易。

static public byte[] DecryptAES(byte[] toDecryptAES, byte[] AESKeyAndIV, RSAParameters RSAPrivateKey)
{
    byte[] AESKey = RSADecrypt(AESKeyAndIV, RSAPrivateKey);

    using (MemoryStream source = new MemoryStream(toDecryptAES))
    {
        using (RijndaelManaged AES = new RijndaelManaged())
        {
            AES.KeySize = 256;
            AES.BlockSize = 128;
            AES.Key = AESKey;
            var iv = ReadFully(source, AES.KeySize);
            AES.IV = iv;
            AES.Mode = CipherMode.CBC;

            using (var cs = new CryptoStream(source, AES.CreateDecryptor(), CryptoStreamMode.Read))
            {
                using(var dest = new MemoryStream())
                {
                    cs.CopyTo(dest);
                    return dest.ToArray();
                }
            }
        }
    }
}

For other readers, note that RSAEncrypt and RSADecrypt are wrappers for calls to the RSACryptoServiceProvider .对于其他读者,请注意RSAEncryptRSADecrypt是调用RSACryptoServiceProvider包装器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM