I am trying to write a simple program that allows to encrypt a string of plaintext so that it can be transmitted via an "insecure channel" by which I mean a "regular chat"; be it Skype, Facebook, or even email or something similar. This is what I came up with:
At first, one user, Alice, will generate a cryptographically secure key for this "session" (which can - I think - be very long, so long as new IVs can be generated). It will be used for the AES algorithm.
byte[] sessionKey = GenerateNewKey();
I/You don't need to worry about GenerateNewKey()
.
This key needs to be transferred. RSA should be used as the asymmetric encryption algorithm. At first, Bob will extract a new private-public Key set RSAParameters
from a RSACryptoServiceProvider . Furthermore, I will take the public key as MyPublicKey
from that RSAParameters
.
RSAParameters extractedParameters;
using (RSACryptoServiceProvider prov = new RSACryptoServiceProvider(
RsaKeyLength /* const, >= 4096 */)
{
try
{
extractedParameters = prov.ExportParameters(true);
}
finally
{
prov.PersistKeyInCsp = false;
}
}
byte[] bobsPublicKey = extractedParameters.Modulus;
Now, this byte[] bobsPublicKey
will be given to Alice, by writing it into the chat. She will encrypt the byte[] sessionKey
as follows:
byte[] encryptedAES;
using (RSACryptoServiceProvider prov = new RSACryptoServiceProvider())
{
try
{
prov.PersistKeyInCsp = false;
RSAParameters param = prov.ExportParameters(false);
param.Modulus = bobsPublicKey;
prov.ImportParameters(param);
prov.PersistKeyInCsp = false;
encryptedAES = prov.Encrypt(sessionKey, true /* use OAEP */);
}
finally
{
prov.PersistKeyInCsp = false;
}
}
Then, she will write encryptedAES
into the chat, which will be received by Bob, who finally gets the AES key sessionKey
back via:
byte[] sessionKey;
using (RSACryptoServiceProvider prov = new RSACryptoServiceProvider())
{
try
{
prov.PersistKeyInCsp = false;
prov.ImportParameters(extractedParameters);
prov.PersistKeyInCsp = false;
sessionKey = prov.Decrypt(encryptedAES, true /* use OAEP */);
}
finally
{
prov.PersistKeyInCsp = false;
}
}
Now, lets say Alice wants to send a message to Bob. Assume that for example _aes = "AES, 256 key, CBC, PKCS7"
byte[] iv = GenerateIV(8 /*byte length*/);
byte[] encryptedMessage;
using (MemoryStream ms = new MemoryStream())
{
ms.Write(iv, 0, iv.Length); // Prepend the IV to the Stream
// Append the encrypted message
using (ICryptoTransform trans = _aes.CreateEncryptor(sessionKey, iv))
using (CryptoStream cs = new CryptoStream(ms, trans, CryptoStreamMode.Write)
using (StreamWriter sw = new StreamWriter(cs))
{
sw.Write(message);
sw.Flush();
cs.FlushFinalBlock();
}
encryptedMessage = ms.ToArray();
}
encryptedMessage
will be written to the chat. Bob decypts like:
// extract the IV
byte[] iv = GetIV(encryptedMessage);
// the rest will be the actual message
byte[] data = GetData(encryptedMessage); // Don't worry about those two
byte[] decryptedMessage;
using (MemoryStream ms = new MemoryStream(data, iv.Length, 0))
using (ICryptoTransform trans = _aes.CreateDecryptor(sessionKey, iv))
using (CryptoStream cs = new CryptoStream(ms, trans, CryptoStreamMode.Read)
using (StreamReader sr = new StreamReader(cs))
decryptedMessage = sr.ReadToEnd();
And finally, decryptedMessage
should contain the message Alice tried to send.
My request is, that this code should be reviewed regarding
- whether this is a correct - that is secure - implementation of such an "algorithm pair" for transmitting messages via an unsafe and permanently visible channel
- whether there are redundant calls to the .Net classes
- (more things that can be improved regarding the first point)