生成AES 256位密钥值

13

有人知道如何从任意长度的密码短语生成一个256位的密钥值吗?由于加密后的值需要在数据库中再次生成并进行比较,因此无法加盐。因此,每次加密时必须生成相同的加密字符串。

目前我正在使用一个32字符键,可能错误地假设这是256位?

所以,我想要将“the quick brown fox”转换为适当的AES 256位密钥?


3
“加密无法被加盐,因为需要重新生成加密值。”这就是为什么通常将盐和哈希密码一起存储的原因。这样,如果两个不同的用户使用相同的密码,则会产生不同的哈希值,但对于单个用户来说,相同的密码始终会产生相同的哈希值。 - svick
坦白地说,如果没有盐,它实际上并没有被加密。不应该直接对加密值进行数据库查找。 - Matthew
6个回答

27

您可以使用任意大小的密码构造Rfc2898DeriveBytes类,然后根据需要获得所需大小(256位或32字节)的密钥:

private static byte[] CreateKey(string password, int keyBytes = 32)
{
    const int Iterations = 300;
    var keyGenerator = new Rfc2898DeriveBytes(password, Salt, Iterations);
    return keyGenerator.GetBytes(keyBytes);
}
为了产生确定性输出(即相同的输入将产生相同的输出),您需要硬编码salt。盐必须至少为8个字节:

要产生确定性输出(即相同的输入将产生相同的输出),您需要硬编码盐。盐必须至少为8个字节:

private static readonly byte[] Salt = 
    new byte[] { 10, 20, 30 , 40, 50, 60, 70, 80};

请看上述理论描述的更详细解释。请注意,Rfc2898DeriveBytes 实现了 PBKDF2 :) - Maarten Bodewes
谢谢 - 这非常有帮助。但是,keySize应该是32才能获得一个32字节的密钥数组吗? - SteveB
是的,我想那可能是ByteBlast的一个小错误。我还建议您使用UTF-8编码将密码编码为字节,因为Rfc2898DeriveBytes函数没有明确指定它使用的编码方式。当您从另一个运行时使用该函数时,这是棘手的。 - Maarten Bodewes

2
可能最好的方法是使用PBKDF2和SHA256(它会生成256位的输出),应用程序特定的盐和迭代次数。您应该意识到,使用特定于应用程序的盐会大大减少PBKDF2的保护力度,因此您可能需要采取其他措施来缓解这个问题。一种方法是确保数据库是安全的,并且可以使用最大数量的尝试次数。

您的观点是正确的,32个字符的密码短语不是256位密钥。它不包含足够的熵,有些字节甚至可能没有有效的字符表示。


请参考ByteBlasts的答案以获取上述实现 :) - Maarten Bodewes
谢谢 - 所以上面的方法返回的32字节数组是256位密钥? - SteveB
这是一种基于密码的密钥派生函数,也就是PBKDF的意思。你输入一个密码,然后会返回一个密钥(以字节为单位)。它使用盐和一定数量的迭代进行保护,这使得计算密钥相对困难(无论是对于你还是攻击者都是如此)。攻击者可能需要做很多次才能得到密钥。但是最好使用SHA-256或以上,因为对于任何大于一个哈希输出的情况,都需要完成全部迭代次数——这只会影响你,而不是攻击者。 - Maarten Bodewes

0
 private static IBuffer GetMD5Hash(string key)
    {
        IBuffer bufferUTF8Msg = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8);
        HashAlgorithmProvider hashAlgorithmProvider = HashAlgorithmProvider.OpenAlgorithm(HashAlgorithmNames.Md5);
        IBuffer hashBuffer = hashAlgorithmProvider.HashData(bufferUTF8Msg);
        if (hashBuffer.Length != hashAlgorithmProvider.HashLength)
        {
            throw new Exception("There was an error creating the hash");
        }
        return hashBuffer;
    }

    #region Static

    public static string GenerateKey(string password, int resultKeyLength = 68)
    {
        if (password.Length < 6)
            throw new ArgumentException("password length must atleast 6 characters or above");
        string key = "";

        var hashKey = GetMD5Hash(password);
        var decryptBuffer = CryptographicBuffer.ConvertStringToBinary(password, BinaryStringEncoding.Utf8);
        var AES = SymmetricKeyAlgorithmProvider.OpenAlgorithm(SymmetricAlgorithmNames.AesEcbPkcs7);
        var symmetricKey = AES.CreateSymmetricKey(hashKey);
        var encryptedBuffer = CryptographicEngine.Encrypt(symmetricKey, decryptBuffer, null);
        key = CryptographicBuffer.EncodeToBase64String(encryptedBuffer);
        string cleanKey  = key.Trim(new char[] { ' ', '\r', '\t', '\n', '/', '+', '=' });
        cleanKey = cleanKey.Replace("/", string.Empty).Replace("+", string.Empty).Replace("=", string.Empty);
        key = cleanKey;
        if(key.Length > resultKeyLength)
        {
           key = key.Substring(0, Math.Min(key.Length, resultKeyLength));
        }
        else if(key.Length == resultKeyLength)
        {
            return key;
        }
        else if (key.Length < resultKeyLength)
        {
            key = GenerateKey(key);
        }
        return key;

    }

//获取前44个字符作为AES密钥,剩余字符作为AES IV


0
public static string GenerateBitKey(int letterCount = 44)
    {
        // Get the number of words and letters per word.
        int num_letters = letterCount;
        // Make an array of the letters we will use.
        char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray();

        // Make a random number generator.
        Random rand = new Random();

        // Make the words.
        // Make a word.
        string word = "";
        for (int j = 1; j <= num_letters; j++)
        {
            // Pick a random number between 0 and 25
            // to select a letter from the letters array.
            int letter_num = rand.Next(0, letters.Length - 1);

            // Append the letter.
            word += letters[letter_num];
        }
        return word;
    }

这更像是生成随机密钥的代码,而不是将口令字符串可重复地转换为派生密钥的代码。 - Toby Speight
生成密钥的方法请查看我下面的回复。 - Minute V

-1

您可以使用一些哈希函数,从任意长度的输入提供256位输出,例如SHA256。


2
官方上说,哈希不是基于密码的密钥派生函数,因此不应直接用作此类目的。 - Maarten Bodewes

-1

我的版本。我只想要没有密码的密钥。

    public static string GenerateBitKey(int letterCount = 44)
    {
        // Get the number of words and letters per word.
        int num_letters = letterCount;
        // Make an array of the letters we will use.
        char[] letters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrsruvwxyz+".ToCharArray();
        int lettersLength =  letters.Length;

        // Make a word.
        string word = "";

        //Use Cryptography to generate random numbers rather than Psuedo Random Rand
        // Deliberate overkill here
        byte[] randomBytes = new byte[num_letters*256];


        List<int> rands = new List<int>();
        do
        {
            using (System.Security.Cryptography.RNGCryptoServiceProvider rngCsp = new
                        System.Security.Cryptography.RNGCryptoServiceProvider())
            {
                // Fill the array with a random value.
                rngCsp.GetBytes(randomBytes);
            }


            // Truncate the set of random bytes to being in range 0 .. (lettersLength-1)
            // Nb Using mod of randomBytes will reduce entropy of the set

            foreach (var x in randomBytes)
            {
                if (x < lettersLength)
                    rands.Add((int)x);
                if (rands.Count()==num_letters)
                     break;
            }
        }
        while (rands.Count < letterCount);


        int[] randsArray = rands.ToArray();

        // Get random selection of characters from letters
        for (int j = 0; j < num_letters; j++)
        {
            int letter_num = randsArray[j];
            // Append the letter.
            word += letters[letter_num];
        }
        return word;
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接