首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Rijndael文件加密/解密

Rijndael文件加密/解密
EN

Stack Overflow用户
提问于 2015-06-11 09:19:20
回答 1查看 1.4K关注 0票数 0

在过去的几天里,我基于RijndaelManaged类中可用的Rijndael加密标准创建了一个文件加密/解密类,并浏览了我可以找到的所有资源和示例。这些例子要么是过时的,要么是破碎的,要么是有限的,但至少已经学到了很多东西,我认为我会在确保它的强大性和超越你的批评之后,发布一个最新版本的。

到目前为止,我发现的唯一问题是,需要知道salt无法像字符串那样存储在加密的文件中,除非您将每字节的读/写转换为基于读/写的缓冲区,但是在解密时需要满足这一点,而且还需要至少4字节的数据来加密(虽然我并不认为这是一个问题,但需要提及)。

我也不完全确定,一个盐是否足以满足密钥和初始化向量,还是两个更好的安全原因?

任何其他意见和/或优化也将是非常感谢的。

代码语言:javascript
复制
class FileEncDec
{
    private int keySize;
    private string passPhrase;

    internal FileEncDec( int keySize = 256, string passPhrase = @"This is pass phrase key to use for testing" )
    {
        this.keySize = keySize;
        this.passPhrase = passPhrase; // Can be user selected and must be kept secret
    }

    private static byte[] GenerateSalt( int length )
    {
        byte[] salt = new byte[ length ];

        // Populate salt with cryptographically strong bytes.
        RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();

        rng.GetNonZeroBytes( salt );

        // Split salt length (always one byte) into four two-bit pieces and store these pieces in the first four bytes 
        // of the salt array.
        salt[ 0 ] = (byte)( ( salt[ 0 ] & 0xfc ) | ( length & 0x03 ) );
        salt[ 1 ] = (byte)( ( salt[ 1 ] & 0xf3 ) | ( length & 0x0c ) );
        salt[ 2 ] = (byte)( ( salt[ 2 ] & 0xcf ) | ( length & 0x30 ) );
        salt[ 3 ] = (byte)( ( salt[ 3 ] & 0x3f ) | ( length & 0xc0 ) );

        return salt;
    }

    internal bool EncryptFile( string inputFile, string outputFile )
    {
        try
        {
            byte[] salt = GenerateSalt( 16 ); // Salt needs to be known for decryption (can be safely stored in the file)
            Rfc2898DeriveBytes derivedBytes = new Rfc2898DeriveBytes( passPhrase, salt, 10000 );
            int bytesRead, bufferSize = keySize / 8;
            byte[] data = new byte[ bufferSize ];
            RijndaelManaged cryptor = new RijndaelManaged();
            cryptor.Key = derivedBytes.GetBytes( keySize / 8 );
            cryptor.IV = derivedBytes.GetBytes( cryptor.BlockSize / 8 );

            using ( var fsIn = new FileStream( inputFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan ) )
            {
                using ( var fsOut = new FileStream( outputFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.SequentialScan ) )
                {
                    // Add the salt to the file
                    fsOut.Write( salt, 0, salt.Length );

                    using ( CryptoStream cs = new CryptoStream( fsOut, cryptor.CreateEncryptor(), CryptoStreamMode.Write ) )
                    {
                        while ( ( bytesRead = fsIn.Read( data, 0, bufferSize ) ) > 0 )
                        {
                            cs.Write( data, 0, bytesRead );
                        }
                    }
                }
            }

            return true;
        }
        catch ( Exception )
        {
            return false;
        }
    }

    internal bool DecryptFile( string inputFile, string outputFile )
    {
        try
        {
            int bytesRead = 0, bufferSize = keySize / 8, saltLen;
            byte[] data = new byte[ bufferSize ], salt;
            Rfc2898DeriveBytes derivedBytes;
            RijndaelManaged cryptor = new RijndaelManaged();    // Create new cryptor so it's thread safe and don't need to use locks

            using ( var fsIn = new FileStream( inputFile, FileMode.Open, FileAccess.Read, FileShare.Read, 4096, FileOptions.SequentialScan ) )
            {
                // Retrieve the salt length from the file
                fsIn.Read( data, 0, 4 );

                saltLen =   ( data[ 0 ] & 0x03 ) |
                            ( data[ 1 ] & 0x0c ) |
                            ( data[ 2 ] & 0x30 ) |
                            ( data[ 3 ] & 0xc0 );

                salt = new byte[ saltLen ];
                Array.Copy( data, salt, 4 );

                // Retrieve the remaining salt from the file and create the cryptor
                fsIn.Read( salt, 4, saltLen - 4 );
                derivedBytes = new Rfc2898DeriveBytes( passPhrase, salt, 10000 );
                cryptor.Key = derivedBytes.GetBytes( keySize / 8 );
                cryptor.IV = derivedBytes.GetBytes( cryptor.BlockSize / 8 );

                using ( var fsOut = new FileStream( outputFile, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.SequentialScan ) )
                {
                    using ( var cs = new CryptoStream( fsIn, cryptor.CreateDecryptor(), CryptoStreamMode.Read ) )
                    {
                        while ( ( bytesRead = cs.Read( data, 0, bufferSize ) ) > 0 )
                        {
                            fsOut.Write( data, 0, bytesRead );
                        }
                    }
                }
            }

            return true;
        }
        catch ( Exception )
        {
            return false;
        }
    }
}

编辑: 1.添加盐发生器。2.重构为单saltRfc2898DerivedBytes,从password + salt推导出IV。3.使加密/解密线程安全(如果我做错了,请告诉我)。

编辑2: 1.重构,使读/写使用缓冲区,而不是单字节读/写。2.在加密文件中嵌入salt并清除变量(但仍然允许passPhrase默认为“复制/粘贴”示例)。3.重构文件句柄。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-06-11 09:57:08

你应该每次都用不同的静脉输液。如果您使用相同的IV与相同的数据,结果是相同的。攻击者现在可以推断文件(部分)是相同的,这是一个漏洞。您可以生成16个强随机字节,并将它们用作Rfc2898DeriveBytes的salt。将这些字节准备到文件中。只使用一个Rfc2898DeriveBytes来生成IV和key。或者,您可以对密钥完全不使用salt,并随机生成IV。salt可用于使密钥派生对您的用例而言是唯一的,或者例如,为应用程序的每个用户提供不同的密钥派生算法。

注意,按字节顺序处理流的速度非常慢。使用缓冲器。也许,您应该使用Stream.Copy

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30776606

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档