How to generate Rijndael KEY and IV using a passphrase?
Asked Answered
D

6

33

How to generate Rijndael KEY and IV using a passphrase? The key length must be in 256 bits.

Documentation answered 26/6, 2011 at 8:12 Comment(7)
What language are you using? Which library?Rolandrolanda
Thanks for all responses. My upvote for all.Documentation
Don't do this; learn how an IV actually works. See my response on the chosen answer for why this is an abysmal idea.Rasure
@Rasure - Is Alex Aza's answer sufficient? My guess is that the reason for this question is that the ASP.Net Rijndael constructor expects both a key and an IV.Amoritta
@Amoritta Nope. The fundamental premise of the question is wrong. You don't generate an IV from the same source as the key. You generate a unique IV with no relationship to the key every time you encrypt a message, then include that IV with that message. It is impossible to both answer this question and provide a working solution as the question is based on false premises.Rasure
@Amoritta In the case of C#, you can call the GenerateIV() method to get an appropriate IV. Then once you've encrypted the plaintext, add the IV as the first 16 bytes of the ciphertext. When you decrypt the ciphertext, pluck off the first 16 bytes and use those as the IV when decrypting.Rasure
Note: The above assumes Rijndael with a 128-bit block size (AES standard) in CBC mode. It's different if you change any of those factors.Rasure
R
44

This is plug and play code that I found on internet. It just works:

using System.IO;
using System.Security.Cryptography;

private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

public static byte[] Encrypt(byte[] plain, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(plain, 0, plain.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}

public static byte[] Decrypt(byte[] cipher, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(cipher, 0, cipher.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}
Roxanaroxane answered 1/7, 2011 at 5:50 Comment(9)
Ok, I award bounty to new user as encouragementDocumentation
@Gens - I always believed that the best answer should be chosen, the answer that is the most useful for all the users. I spent quite a lot of time for making reusable API and examples. While you make it encouraging for new users, it is absolutely unfair and discouraging for old ones.Blindfold
Regarding the lines- rijndael.Key = pdb.GetBytes(32); rijndael.IV = pdb.GetBytes(16); Does this mean the IV is just the first half of the Key? Does this have security implications?Amoritta
@Amoritta Yes. I suspect it is possible to establish the original key by doing an attack to determine the IV which, if determined (and bear in mind Rijndael doesn't treat the IV as being secret), would allow a brute-force attack against the original password/salt combination (just keep plugging values until you get that IV).Rasure
This doesn't even begin to touch on the problems with using the same IV repeatedly. Don't do this. Every time you re-use an IV, a kitty's entropy rapidly increases at an alarming rate.Rasure
Also, if it is the first half of the key (will check in a second), then recovering the IV would reduce the problem of getting the rest of the key by 17 million trillion times. EDIT: FWIW no, it isn't. However, that doesn't automatically mean you can't potentially recover half of the key from it anyway.Rasure
Could bother to comment rather than just post a piece of code.Blue
Do not reuse the IV!!! Create a new one and append it to the ciphertextVespid
above code throws an error :"Length of the data to decrypt is invalid"Aggression
B
62

I think you are looking for password-based key derivation. There is Rfc2898DeriveBytes class that implements it.

Rfc2898DeriveBytes takes a password, a salt, and an iteration count, and then generates keys through calls to the GetBytes method.

RFC 2898 includes methods for creating a key and initialization vector (IV) from a password and salt. You can use PBKDF2, a password-based key derivation function, to derive keys using a pseudo-random function that allows keys of virtually unlimited length to be generated. The Rfc2898DeriveBytes class can be used to produce a derived key from a base key and other parameters. In a password-based key derivation function, the base key is a password and the other parameters are a salt value and an iteration count.

For more information about PBKDF2, see RFC 2898, "PKCS #5: Password-Based Cryptography Specification Version 2.0,".

Example:

public static byte[] CreateKey(string password)
{
    var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };

    const int Iterations = 9872;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
        return rfc2898DeriveBytes.GetBytes(32);
}

You can use DeriveBytes in any symmetric algorithm, not just Rijndael.
Example:

public static SymmetricAlgorithm InitSymmetric(SymmetricAlgorithm algorithm, string password, int keyBitLength)
{
    var salt = new byte[] { 1, 2, 23, 234, 37, 48, 134, 63, 248, 4 };

    const int Iterations = 234;
    using (var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, salt, Iterations))
    {
        if (!algorithm.ValidKeySize(keyBitLength))
            throw new InvalidOperationException("Invalid size key");

        algorithm.Key = rfc2898DeriveBytes.GetBytes(keyBitLength / 8);
        algorithm.IV = rfc2898DeriveBytes.GetBytes(algorithm.BlockSize / 8);
        return algorithm;
    }
}

private static byte[] Transform(byte[] bytes, Func<ICryptoTransform> selectCryptoTransform)
{
    using (var memoryStream = new MemoryStream())
    {
        using (var cryptoStream = new CryptoStream(memoryStream, selectCryptoTransform(), CryptoStreamMode.Write))
            cryptoStream.Write(bytes, 0, bytes.Length);
        return memoryStream.ToArray();
    }
}

Usage:

public static void Main()
{
    using (var rijndael = InitSymmetric(Rijndael.Create(), "TestPassword", 256))
    {
        var text = "Some text to encrypt";
        var bytes = Encoding.UTF8.GetBytes(text);

        var encryptedBytes = Transform(bytes, rijndael.CreateEncryptor);
        var decryptedBytes = Transform(encryptedBytes, rijndael.CreateDecryptor);

        var decryptedText = Encoding.UTF8.GetString(decryptedBytes);
        Debug.Assert(text == decryptedText);
    }
}

Make sure you change salt and iterations parameters.

Blindfold answered 30/6, 2011 at 7:11 Comment(2)
Do you need to pass in a random salt for each usage of this method? Can you do this instead: Use a constant salt, just like in your example, but derive the IV separately, using a different password. For example, if you had a User record, could you derive the IV from a constant salt and the User's ID? That would avoid having to store the salt somewhere, and you could instead rely on existing data (in this one case)Palmation
As mentioned in @Rushyo's comment on the question, the IV should not be derived from the password, rather, call GenerateIV, then pre-pend this to the encrypted string.Amoritta
R
44

This is plug and play code that I found on internet. It just works:

using System.IO;
using System.Security.Cryptography;

private static readonly byte[] SALT = new byte[] { 0x26, 0xdc, 0xff, 0x00, 0xad, 0xed, 0x7a, 0xee, 0xc5, 0xfe, 0x07, 0xaf, 0x4d, 0x08, 0x22, 0x3c };

public static byte[] Encrypt(byte[] plain, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(plain, 0, plain.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}

public static byte[] Decrypt(byte[] cipher, string password)
{
    MemoryStream memoryStream;
    CryptoStream cryptoStream;
    Rijndael rijndael = Rijndael.Create();
    Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, SALT);
    rijndael.Key = pdb.GetBytes(32);
    rijndael.IV = pdb.GetBytes(16);
    memoryStream = new MemoryStream();
    cryptoStream = new CryptoStream(memoryStream, rijndael.CreateDecryptor(), CryptoStreamMode.Write);
    cryptoStream.Write(cipher, 0, cipher.Length);
    cryptoStream.Close();
    return memoryStream.ToArray();
}
Roxanaroxane answered 1/7, 2011 at 5:50 Comment(9)
Ok, I award bounty to new user as encouragementDocumentation
@Gens - I always believed that the best answer should be chosen, the answer that is the most useful for all the users. I spent quite a lot of time for making reusable API and examples. While you make it encouraging for new users, it is absolutely unfair and discouraging for old ones.Blindfold
Regarding the lines- rijndael.Key = pdb.GetBytes(32); rijndael.IV = pdb.GetBytes(16); Does this mean the IV is just the first half of the Key? Does this have security implications?Amoritta
@Amoritta Yes. I suspect it is possible to establish the original key by doing an attack to determine the IV which, if determined (and bear in mind Rijndael doesn't treat the IV as being secret), would allow a brute-force attack against the original password/salt combination (just keep plugging values until you get that IV).Rasure
This doesn't even begin to touch on the problems with using the same IV repeatedly. Don't do this. Every time you re-use an IV, a kitty's entropy rapidly increases at an alarming rate.Rasure
Also, if it is the first half of the key (will check in a second), then recovering the IV would reduce the problem of getting the rest of the key by 17 million trillion times. EDIT: FWIW no, it isn't. However, that doesn't automatically mean you can't potentially recover half of the key from it anyway.Rasure
Could bother to comment rather than just post a piece of code.Blue
Do not reuse the IV!!! Create a new one and append it to the ciphertextVespid
above code throws an error :"Length of the data to decrypt is invalid"Aggression
D
9

The IV must be random (doesn't need to be an unpredictable random, just random enough that they won't be reused).

As to generating the key from the password, you are looking for a key derivation function for which nowadays there are at least three good choices (PBKDF2, bcrypt, scrypt), using a non iterated hash as a previous poster suggests more often than not leads to insecure systems.

Also use AES nor Rijndael, that's not exactly the same thing. Using a Rijndael combination not part of AES could be an inter-operability nightmare later, and the security of those functions combination isn't well studied anyway.

Decern answered 26/6, 2011 at 20:31 Comment(0)
M
4

IV must be random (you usually pass it together with the encrypted data), and the key can be derived in a number of ways: simply pad the password to the length of the key (if the password is shorter than 32 characters) or (which is more reliable) derive a key by using SHA2 hashing algorithm or use some more sophisticated way.

Matson answered 26/6, 2011 at 9:50 Comment(4)
Don't ever pad the password. Don't use String input as a key. Don't use SHA2. Use a key derivation algorithm, such as PKBDF2, which is specifically designed for this problem. Also, be sure to read the label (RTFM) before you use a key derivation algorithm.Rasure
@Rasure don't ever make so all-embracing statements.Blue
Because playing guessing games and hoping things are going to work with cryptographic algorithms has been proven to be such a good idea? May as well pray to the Machine God and recant the holy sacred rites for all the engineering sense that makes. There are very well defined methodologies for key derivation. The fact that people use home-brew nonsense means I get a paycheck as a hacker but that doesn't make it good engineering.Rasure
Hmm. Just had a look at 'BlackBox'. I can see you're a big fan of the DIY approach. GL with that!Rasure
T
1

Use this Rfc2898DeriveBytes Class.

Word of advise though, your security level has dropped/limited by the passphrase length/strength. So, don't do it.

Tref answered 1/7, 2011 at 22:59 Comment(0)
C
0

Other answers contain way more information. The answer to this question where you only want to generate the key and IV given a passphrase use:

        // use something more random in real life
        var salt =  new byte[]{1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16};

        string password = "my-password";
        Rfc2898DeriveBytes pdb = new Rfc2898DeriveBytes(password, salt);
        var key = pdb.GetBytes(32);
        var iv = pdb.GetBytes(16);
Capel answered 7/2, 2020 at 20:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.