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:
- 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
- Encrypting data involves:
- Generating a random AES key
- Encrypting the data with that AES key
- Generating an RSA signature of the encrypted data using the RSA private key of the originator
- 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:
- Public key
- Salt
- Initialisation Vector
- Encrypted private key
pretty much where I want, because the password is needed to actually crack the private key.
Decrypting is relatively simple too:
- Receive incoming data
- RSA verify it against the purported sender's RSA public key
- Decrypt receiver's RSA private key from derived password + salt AES key
- Decrypt access key (embedded / hosted AES key)
- Decrypt received data using the provided key