Clint
Clint

Reputation: 6220

Deriving public / key pair from passphrase

Following the answer available here: https://crypto.stackexchange.com/questions/1662/how-can-one-securely-generate-an-asymmetric-key-pair-from-a-short-passphrase

I've started down the following path:

public static void DeriveKeyPair(string pass, byte[] salt)
{
    using (var derived = new Rfc2898DeriveBytes(pass, salt, 10000))
    {
        var randomNum = new Random(BitConverter.ToInt32(derived.GetBytes(4), 0));
        // Can't seem to find an asymmetric implementation that I can supply the seed to
    }
}

Ignoring the fact that the return type isn't going to do anything useful, the big problem I'm having is that I can't seem to find an asymmetric crypto provider that I can either seed, or provide the seeded number generator to.

I'm doing this so I don't have to store the private key anywhere, this is a security consideration and aids in portability as the keys can be derived on any machine provided the same password (and salt - which is initially unique and random).

Upvotes: 2

Views: 1297

Answers (1)

Clint
Clint

Reputation: 6220

After some hunting around I decided to settle upon generating a random RSA key-pair, and then symmetrically encrypting the private key using AES.

This leads me to the two following methods:

public static byte[] EncryptData(string pass, byte[] salt, byte[] encryptedPrivateKey, byte[] targetPublicKey,
    byte[] iv, byte[] data)
{
    using (var rfc = new Rfc2898DeriveBytes(pass, salt, IterationCount))
    {
        using (var aes = new AesCryptoServiceProvider())
        {
            aes.KeySize = AesKeySize;
            aes.Key = rfc.GetBytes(aes.KeySize / 8);
            aes.IV = iv;

            using (var dec = aes.CreateDecryptor(aes.Key, aes.IV))
            {
                using (var ms = new MemoryStream(encryptedPrivateKey))
                {
                    using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Read))
                    {
                        var privKey = new byte[RsaKeySize];
                        cs.Read(privKey, 0, privKey.Length);
                        return RsaEncrypt(targetPublicKey, data);
                    }
                }
            }
        }
    }
}

public static byte[] DecryptData(string pass, byte[] salt, byte[] encryptedPrivateKey, byte[] iv, byte[] data)
{
    using (var rfc = new Rfc2898DeriveBytes(pass, salt, IterationCount))
    {
        using (var aes = new AesCryptoServiceProvider())
        {
            aes.KeySize = AesKeySize;
            aes.Key = rfc.GetBytes(aes.KeySize/8);
            aes.IV = iv;

            using (var dec = aes.CreateDecryptor(aes.Key, aes.IV))
            {
                using (var ms = new MemoryStream(encryptedPrivateKey))
                {
                    using (var cs = new CryptoStream(ms, dec, CryptoStreamMode.Read))
                    {
                        var privKey = new byte[RsaKeySize];
                        cs.Read(privKey, 0, privKey.Length);
                        return RsaDecrypt(privKey, data);
                    }
                }
            }
        }
    }
}

RSA isn't enough.

Essentially, RSA can only encrypt data that's smaller than the key size

In my new scheme:

  1. User identity is the RSA public key and an RSA private key that has been encrypted with AES by deriving the AES key using the password and salt
  2. Encrypting data involves:
    1. Generating a random AES key
    2. Encrypting the data with that AES key
    3. Generating an RSA signature of the encrypted data using the RSA private key of the originator
    4. Access to the data is granted by RSA encrypting the random AES key with the target's public RSA key

This allows me to store all the core information:

  1. Public key
  2. Salt
  3. Initialisation Vector
  4. Encrypted private key

pretty much where I want, because the password is needed to actually crack the private key.

Decrypting is relatively simple too:

  1. Receive incoming data
  2. RSA verify it against the purported sender's RSA public key
  3. Decrypt receiver's RSA private key from derived password + salt AES key
  4. Decrypt access key (embedded / hosted AES key)
  5. Decrypt received data using the provided key

Upvotes: 2

Related Questions