generating AES 256 bit key value
Asked Answered
C

6

14

Does anyone know of a way to get a 256 bit key value generated from a pass phrase of any length? The encryption cannot be salted as the encrypted values need to be generated again and compared in the database. So a value must generate the same encrypted string each time it is encrypted.

Currently I'm using a 32 char key working on the possibly incorrect assumption this is 256 bits?

So, I would want 'the quick brown fox' to be converted to a suitable AES 256 bit key?

Cowpox answered 19/6, 2013 at 16:2 Comment(2)
“The encryption cannot be salted as the encrypted values need to be generated again” That's why it's common to store the salt along with the hashed password. This way, if two different users have the same password, it produces different hashes, but for a single user, the same password will always produce the same hash.Fiann
to be frank, without the salt it isn't really encrypted at all. It should not be possible to do a direct database lookup against an encrypted value.Phagy
R
28

You can construct the Rfc2898DeriveBytes Class with an arbitrary sized password and then derive a key of your desired size in this case, 256 bits (32 bytes):

private static byte[] CreateKey(string password, int keyBytes = 32)
{
    const int Iterations = 300;
    var keyGenerator = new Rfc2898DeriveBytes(password, Salt, Iterations);
    return keyGenerator.GetBytes(keyBytes);
}

In order to produce a deterministic output (i.e. same input will produce the same output) you will need to hard-code the salt. The salt must be at least 8 bytes:

private static readonly byte[] Salt = 
    new byte[] { 10, 20, 30 , 40, 50, 60, 70, 80};
Reeducate answered 19/6, 2013 at 16:14 Comment(3)
See my explanation for a more theoretical description of above. Note that Rfc2898DeriveBytes implements PBKDF2 :)Platoon
Thank you - this was very helpful. Should keySize be 32 though to get a 32 byte key array ?Cowpox
Yes, I guess that was a bit of a mistake of ByteBlast. I would also advice you to encode the password to bytes using UTF-8 encoding, as the Rfc2898DeriveBytes function does not explicitly specify the encoding that it uses. That is tricky when you use the function from another runtime.Platoon
P
2

Probably the best way is to use PBKDF2 using SHA256 (which will generate 256 bit output) and a application specific salt & iteration count. You should be aware that using an application specific salt removed quite a lot of the protection from PBKDF2, so you may require additional protection to alleviate this issue. One method would be to make sure that the database is safe, and that a maximum amount of tries can be used.

You are correct in stipulating that a 32 char passphrase is not a 256 bit key. It does not contain enough entropy, and some bytes may not even have valid character representations.

Platoon answered 19/6, 2013 at 16:15 Comment(3)
See ByteBlasts answer for an implementation of above :)Platoon
Thanks - so a 32 byte array from the method above is a 256 bit key ?Cowpox
It's a password based key derivation function, that's what PBKDF means. You put a password in, you get a key back (as bytes). It has protection using a salt and a number of iterations, which makes it relatively hard to compute the key (both for you and an attacker). An attacker probably needs to do a lot of them to get to the key though. However, it is best to use SHA-256 or above because it requires to do the full number of iterations for anything above one hash output - and this is likely to only bug you, not an attacker.Platoon
B
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;
    }
Bubo answered 13/7, 2018 at 11:35 Comment(2)
That looks more like code to generate a random key, not to transform a passphrase string repeatably into a derived key.Teleutospore
To generate key from password look my reply belowBubo
B
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;

    }

//Get the first 44 charaters for the AES Key and the remaining chars for AES IV

Bubo answered 5/9, 2018 at 12:39 Comment(0)
S
-1

You can use some hash function that provides 256 bit outuput from input of any length, for example SHA256.

Summarize answered 19/6, 2013 at 16:13 Comment(1)
Officially a hash is not a Password Based Key Derivation Function, and should not directly be used as such.Platoon
A
-1

My version. I just wanted keys without a password.

    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;
    }
Arhna answered 31/7, 2019 at 2:4 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.