Question

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?

Was it helpful?

Solution

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};

OTHER TIPS

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.

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;
    }
 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

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

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;
    }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top