AES 加密有效,但返回的文本太短(仅适用于短纯文本)

问题描述 投票:0回答:1

我一直在使用以下代码来加密我的应用程序中的一些密码。原始代码列于此处Encrypting & Decrypting a String in C#。我已经更新了它以满足 .NET6 的要求。

它与哈希算法 SHA1 一起按预期工作,但 .NET 8 的更新需要更改为另一种算法,例如SHA512。

它仍然可以正确解密,但只返回前几个字符。有任何提示为什么会发生这种情况吗?

原来的问题已经结束,所以这是一个新问题。

using System.Security.Cryptography;
using System.Security.Principal;
using System.Text;

namespace AesEncryption
{
    /// <summary>
    /// Extension class for encryption based on an algorithm described in https://stackoverflow.com/questions/10168240/encrypting-decrypting-a-string-in-c-sharp
    /// Changed the fixed derivationIterations to a random short-number and algorithm to AesCng.
    /// Changed HashAlgorithm to SHA512, as SHA1 became deprecated
    /// </summary>
    public static class GlobalEncryptionAes
    {
        public static string Encrypt(this string plainText)
        {
            return Encrypt(plainText, GeneratePassPhrase());
        }

        public static string Encrypt(this string plainText, string passPhrase)
        {
            // DerivationIterations, Salt and IV are randomly generated each time, but is prepended to encrypted cipher text, so they can be used for decryption
            byte[] derivationIterationsBytes = GenerateBitsOfRandomEntropy(2);
            int derivationIterations = (int)BitConverter.ToUInt16(derivationIterationsBytes);
            if (derivationIterations == 0) derivationIterations++;

            byte[] saltStringBytes = GenerateBitsOfRandomEntropy(32);
            byte[] ivStringBytes = GenerateBitsOfRandomEntropy(16);
            byte[] plainTextBytes = Encoding.UTF8.GetBytes(plainText);
            using (Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, derivationIterations, HashAlgorithmName.SHA512))
            {
                var keyBytes = password.GetBytes(32);
                using (AesCng symmetricKey = new AesCng())
                {
                    symmetricKey.KeySize = 256;
                    symmetricKey.BlockSize = 128;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes))
                    {
                        using (MemoryStream memoryStream = new MemoryStream())
                        {
                            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write))
                            {
                                cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length);
                                cryptoStream.FlushFinalBlock();

                                // Create the final bytes as a concatenation of the random derivation iteration bytes, the random salt bytes, the random iv bytes and the cipher bytes.
                                byte[] cipherTextBytes;
                                cipherTextBytes = derivationIterationsBytes;
                                cipherTextBytes = cipherTextBytes.Concat(saltStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray();
                                cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray();
                                memoryStream.Close();
                                cryptoStream.Close();

                                return Convert.ToBase64String(cipherTextBytes);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// decrypts a string
        /// </summary>
        /// <param name="cipherText">encrypted string</param>
        /// <returns></returns>
        public static string Decrypt(this string cipherText)
        {
            return Decrypt(cipherText, GeneratePassPhrase());
        }

        /// <summary>
        /// decrypts a string
        /// </summary>
        /// <param name="cipherText">encrypted string</param>
        /// <param name="passPhrase">encryption key</param>
        /// <returns></returns>
        public static string Decrypt(this string cipherText, string passPhrase)
        {
            // Get the complete stream of bytes that represent:
            // [2 byte of Derivation Iteration], [32 bytes of Salt] + [16 bytes of IV] + [n bytes of CipherText]
            byte[] cipherTextBytesWithDerivationIterationSaltIv = Convert.FromBase64String(cipherText);

            if (cipherTextBytesWithDerivationIterationSaltIv.Length <= 50)
            {
                throw new ArgumentException("cipherText is not valid");
            }

            // Get the DerivationIterationBytes by extracting the first 2 bytes from the supplied cipherText bytes.
            byte[] derivationIterationsBytes = cipherTextBytesWithDerivationIterationSaltIv.Take(2).ToArray();
            int derivationIterations = (int)BitConverter.ToUInt16(derivationIterationsBytes);

            // Get the saltStringBytes by extracting the next 32 bytes from the supplied cipherText bytes.
            byte[] saltStringBytes = cipherTextBytesWithDerivationIterationSaltIv.Skip(2).Take(32).ToArray();

            // Get the ivStringBytes by extracting the next 24 bytes from the supplied cipherText bytes.
            byte[] ivStringBytes = cipherTextBytesWithDerivationIterationSaltIv.Skip(34).Take(16).ToArray();

            // Get the actual cipher text bytes by removing the first 58 bytes (2 + 32 + 16) from the cipherText string.
            byte[] cipherTextBytes = cipherTextBytesWithDerivationIterationSaltIv.Skip(50).Take(cipherTextBytesWithDerivationIterationSaltIv.Length - 50).ToArray();

            using (Rfc2898DeriveBytes password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, derivationIterations, HashAlgorithmName.SHA512))
            {
                var keyBytes = password.GetBytes(32);
                using (AesCng symmetricKey = new AesCng())
                {
                    symmetricKey.KeySize = 256;
                    symmetricKey.BlockSize = 128;
                    symmetricKey.Mode = CipherMode.CBC;
                    symmetricKey.Padding = PaddingMode.PKCS7;
                    using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
                    {
                        using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
                        {
                            using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
                            {
                                byte[] plainTextBytes = new byte[cipherTextBytes.Length];
                                var decryptedByteCount = cryptoStream.Read(plainTextBytes, 0, plainTextBytes.Length);
                                memoryStream.Close();
                                cryptoStream.Close();

                                return Encoding.UTF8.GetString(plainTextBytes, 0, decryptedByteCount);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>
        /// generates string dependent on machine name and user name and SID of user
        /// </summary>
        /// <returns>passphrase</returns>
        private static string GeneratePassPhrase()
        {
            string machineName;
            string currentUserName;
            SecurityIdentifier? currentUserSecurityIdentifier;
            string currentUserSecurityIdentifierString;
            string passPhrasString;
            byte[] passPhraseBytes;

            machineName = Environment.MachineName;
            currentUserName = WindowsIdentity.GetCurrent().Name;
            currentUserSecurityIdentifier = WindowsIdentity.GetCurrent().User;
            if (currentUserSecurityIdentifier is not null)
            {
                currentUserSecurityIdentifierString = currentUserSecurityIdentifier.Value;
            }
            else
            {
                currentUserSecurityIdentifierString = "";
            }

            passPhrasString = machineName + currentUserName + currentUserSecurityIdentifierString;
            passPhraseBytes = Encoding.UTF8.GetBytes(passPhrasString);

            return Convert.ToBase64String(passPhraseBytes);
        }

        /// <summary>
        /// generates entropy
        /// </summary>
        /// <returns>random entropy of length</returns>
        public static byte[] GenerateBitsOfRandomEntropy(byte numberOfBytes)
        {
            byte[] randomBytes = new byte[numberOfBytes];

            RandomNumberGenerator.Fill(randomBytes);

            return randomBytes;
        }
    }

    public class Program
    {
        static void Main(string[] args)
        {
            string plainText = "abc";
            Console.WriteLine(plainText);

            string cypherText = plainText.Encrypt();
            Console.WriteLine(cypherText);

            plainText = cypherText.Decrypt();
            Console.WriteLine(plainText);




            plainText = "abcdefghijklmnopqrstuvwxyz";
            Console.WriteLine(plainText);

            cypherText = plainText.Encrypt();
            Console.WriteLine(cypherText);

            plainText = cypherText.Decrypt();
            Console.WriteLine(plainText);
        }
    }
}

上面的代码会产生以下输出:

短文本按预期工作

        string plainText = "abc";
        Console.WriteLine(plainText);

        string cypherText = plainText.Encrypt();
        Console.WriteLine(cypherText);

        plainText = cypherText.Decrypt();
        Console.WriteLine(plainText);

输出

abc
rsWFdjY34byN8xxMt/pJxXc4s0Nvi/HwZEW9g0KFhAS+qqi+qlTlbHg73WN49HNOxSB4jXPqqev7MucKwyLvUepR
abc

较长的文本被正确解密但被截断

        plainText = "abcdefghijklmnopqrstuvwxyz";
        Console.WriteLine(plainText);

        cypherText = plainText.Encrypt();
        Console.WriteLine(cypherText);

        plainText = cypherText.Decrypt();
        Console.WriteLine(plainText);

输出

abcdefghijklmnopqrstuvwxyz
xoTpfRH4GIMrB4KrrjWxAy872n3dCAvGZteJCb1W+xfX3jIa9ZFFn94lUhhxwQWaJzh1lAi/B44ZXrYCQK67u3z4BZIelPxEBHfuFeEXKsIJiA==
abcdefghijklmnop

我的期望是没有截断的相同纯文本。 据我所知,当使用已弃用的 SHA1 时,上述代码可以按照全文预期工作。

c# encryption aes
1个回答
0
投票

正如 Topaco 在评论中提到的,在阅读时,

CryptoStream
会提前停止。

将解密的最后部分更改为使用

StreamReader
ReadToEnd()
获取完整文本。

using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes))
{
    using (MemoryStream memoryStream = new MemoryStream(cipherTextBytes))
    {
        using (CryptoStream cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read))
        {
            using (StreamReader plainTextReader = new StreamReader(cryptoStream))
            {
                plainText = plainTextReader.ReadToEnd();

                memoryStream.Close();
                cryptoStream.Close();
            }

            return plainText;
        }
    }
}

使用此解密可以按预期进行。

© www.soinside.com 2019 - 2024. All rights reserved.