4

我已经编写了一个过程,其中文件被加密并上传到 Azure,然后必须解密下载过程,这会失败并出现“填充无效且无法删除”错误或“要解密的数据长度为无效的。” 错误。

我在网上尝试了许多解决方案,包括C# Decrypting mp3 file using RijndaelManaged 和 CryptoStream,但它们似乎都不起作用,我最终只是在这两个错误之间来回跳动。加密过程使用与解密相同的密钥/IV 对,并且由于它会解密流的一部分,我觉得它工作正常 - 它最终会因上述错误而死亡。

这是我的代码,有什么想法吗?请注意,这三个变体(cryptoStream.CopyTo(decryptedStream)do {}while没有一起运行 - 它们在这里显示我已经尝试过的选项,所有这些选项都失败了。

byte[] encryptedBytes = null;

using (var encryptedStream = new MemoryStream())
{
    //download from Azure
    cloudBlockBlob.DownloadToStream(encryptedStream);

    //reset positioning for reading it back out
    encryptedStream.Position = 0;

    encryptedBytes = encryptedStream.ConvertToByteArray();
}

//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
    //stream where decrypted contents will be stored
    using (var decryptedStream = new MemoryStream())
    {
        using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
        {
            using (var decryptor = aes.CreateDecryptor())
            {
                //decrypt stream and write it to parent stream
                using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
                {
                    //fails here with "Length of the data to decrypt is invalid." error
                    cryptoStream.CopyTo(decryptedStream);

                    int data;

                    //fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
                    //implying it is in fact decrypting part of it, just not everything
                    do
                    {
                        data = cryptoStream.ReadByte();
                        decryptedStream.WriteByte((byte)cryptoStream.ReadByte());
                    } while (!cryptoStream.HasFlushedFinalBlock);

                    //fails here with "Length of the data to decrypt is invalid." error after it loops a number of times,
                    //implying it is in fact decrypting part of it, just not everything
                    while ((data = cryptoStream.ReadByte()) != -1)
                    {
                        decryptedStream.WriteByte((byte)data);
                    }
                }
            }
        }

        //reset position in prep for reading
        decryptedStream.Position = 0;
        return decryptedStream.ConvertToByteArray();
    }
}

提到的评论之一想知道是什么ConvertToByteArray,它只是一个简单的扩展方法:

/// <summary>
/// Converts a Stream into a byte array.
/// </summary>
/// <param name="stream">The stream to convert.</param>
/// <returns>A byte[] array representing the current stream.</returns>
public static byte[] ConvertToByteArray(this Stream stream)
{
    byte[] buffer = new byte[16 * 1024];
    using (MemoryStream ms = new MemoryStream())
    {
        int read;
        while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
        {
            ms.Write(buffer, 0, read);
        }
        return ms.ToArray();
    }
}

代码从来没有达到这一点——它在我达到这一点之前就死了。

4

2 回答 2

9

经过各种博客的反复讨论后,我发现上面的代码中实际上有几个错误困扰着我。首先,加密过程错误地写入了数组——它被一个CryptoStream实例包装,但实际上并没有使用它,所以我将未加密的数据写入 Azure。这是解决此问题的正确途径(fileKey是我为生成密钥/ IV 对而创建的自定义类的一部分,因此无论在何处引用,都可以更改为内置过程RijndaelManaged或您将用于出现的任何其他内容带有密钥/IV 对):

using (var aes = new RijndaelManaged { KeySize = 256, Key = fileKey.Key, IV = fileKey.IV })
{
    using (var encryptedStream = new MemoryStream())
    {
        using (ICryptoTransform encryptor = aes.CreateEncryptor())
        {
            using (CryptoStream cryptoStream = new CryptoStream(encryptedStream, encryptor, CryptoStreamMode.Write))
            {
                using (var originalByteStream = new MemoryStream(file.File.Data))
                {
                    int data;
                    while ((data = originalByteStream.ReadByte()) != -1)
                        cryptoStream.WriteByte((byte)data);
                }
            }
        }

        var encryptedBytes = encryptedStream.ToArray();
        return encryptedBytes;
    }
}

其次,由于我的加密过程涉及多个步骤(每个文件总共三个密钥 - 容器、文件名和文件本身),当我尝试解密时,我使用了错误的密钥(在上面提到blobKey解密时可以看到,这实际上是用于加密文件名而不是文件本身的密钥。正确的解密方法是:

//used for the blob stream from Azure
using (var encryptedStream = new MemoryStream(encryptedBytes))
{
    //stream where decrypted contents will be stored
    using (var decryptedStream = new MemoryStream())
    {
        using (var aes = new RijndaelManaged { KeySize = 256, Key = blobKey.Key, IV = blobKey.IV })
        {
            using (var decryptor = aes.CreateDecryptor())
            {
                //decrypt stream and write it to parent stream
                using (var cryptoStream = new CryptoStream(encryptedStream, decryptor, CryptoStreamMode.Read))
                {
                    int data;

                    while ((data = cryptoStream.ReadByte()) != -1)
                        decryptedStream.WriteByte((byte)data);
                }
            }
        }

        //reset position in prep for reading
        decryptedStream.Position = 0;
        return decryptedStream.ConvertToByteArray();
    }
}

我研究了 Azure 加密扩展 ( http://www.stefangordon.com/introducing-azure-encryption-extensions/ ),但它比我感兴趣的更以本地文件为中心——我这边的一切都是流仅 /in-memory ,并且改造该实用程序将比其价值更多的工作。

希望这可以帮助任何希望加密 Azure blob 且对底层文件系统的依赖为零的人!

于 2015-07-20T15:09:13.313 回答
2

派对迟到了,但如果这对找到此线程的人有用:

以下对我很有效。

internal static byte[] AesEncryptor(byte[] key, byte[] iv, byte[] payload)
    {
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;

            aesAlg.Padding = PaddingMode.PKCS7;

            var encryptor = aesAlg.CreateEncryptor(key, iv);

            var encrypted = encryptor.TransformFinalBlock(payload, 0, payload.Length);

            return iv.Concat(encrypted).ToArray();
        }
    }

并解密:

internal static byte[] AesDecryptor(byte[] key, byte[] iv, byte[] payload)
    {
        using (var aesAlg = Aes.Create())
        {
            aesAlg.Mode = CipherMode.CBC;

            aesAlg.Padding = PaddingMode.PKCS7;

            var decryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);

            return decryptor.TransformFinalBlock(payload, 0, payload.Length);
        }
    }

这适用于从hexto解码时加密/解密固定长度的十六进制字符串byte[]以及使用Encoding.UTF8.GetBytes().

于 2018-06-11T10:47:05.767 回答