I can use the following code to
- generate keypairs for two users
- the first user encrypts a message using their private key and the second user's public key
- the second user decrypts the message using their private key and the first user's public key
This works great for 1 to 1 encrypted communication but I want to know how to extend this so that the first user can encrypt a single message for both the second user and the third user. The message should be decryptable using the second user's private key or the third user's private key.
public class EncryptionService
{
private IBlockCipher engine = new DesEngine(); //the cipher engine for encryption
private IAsymmetricCipherKeyPairGenerator keyGen = new X25519KeyPairGenerator(); //keypair generator for X25519 key agreement scheme
private AsymmetricKeyParameter myPrivateKey;
private AsymmetricKeyParameter myPublicKey;
private AsymmetricKeyParameter opponentPrivateKey;
private AsymmetricKeyParameter opponentPublicKey;
public void RunExchange()
{
var plainTextMessage = AsciiStringToBytes("Hello Sir,");
GenerateKeys();
var sharedSecret = GetSharedSecret(myPrivateKey, opponentPublicKey);
Console.WriteLine($"Shared secret: {BytesToAsciiString(sharedSecret)}");
var encryptedMessage = Encrypt(sharedSecret, plainTextMessage);
Console.WriteLine($"Encrypted message: {BytesToAsciiString(encryptedMessage)}");
var decryptedMessage = Decrypt(sharedSecret, encryptedMessage);
Console.WriteLine($"Decrypted message: {BytesToAsciiString(decryptedMessage)}");
var opponentSharedSecret = GetSharedSecret(opponentPrivateKey, myPublicKey);
Console.WriteLine($"Opponent shared secret: {BytesToAsciiString(opponentSharedSecret)}");
var opponentDecryptedMessage = Decrypt(opponentSharedSecret, encryptedMessage);
Console.WriteLine($"Opponent decrypted message: {BytesToAsciiString(opponentDecryptedMessage)}");
}
private byte[] GetSharedSecret(AsymmetricKeyParameter myPrivateKey, AsymmetricKeyParameter opponentPublicKey)
{
var keyAgreement = new X25519Agreement();
keyAgreement.Init(myPrivateKey);
byte[] sharedSecret = new byte[keyAgreement.AgreementSize];
keyAgreement.CalculateAgreement(opponentPublicKey, sharedSecret, 0);
Console.WriteLine(Hex.ToHexString(sharedSecret));
return sharedSecret;
}
private (AsymmetricKeyParameter privateKey, AsymmetricKeyParameter publicKey) GenerateKeyPair()
{
keyGen.Init(new KeyGenerationParameters(new Org.BouncyCastle.Security.SecureRandom(), 256));
var pair = keyGen.GenerateKeyPair();
return (pair.Private, pair.Public);
}
private void GenerateKeys()
{
(myPrivateKey, myPublicKey) = GenerateKeyPair();
(opponentPrivateKey, opponentPublicKey) = GenerateKeyPair();
}
private byte[] Encrypt(byte[] key, byte[] plainTextBytes)
{
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(engine));
cipher.Init(true, new KeyParameter(key));
byte[] rv = new byte[cipher.GetOutputSize(plainTextBytes.Length)];
int tam = cipher.ProcessBytes(plainTextBytes, 0, plainTextBytes.Length, rv, 0);
try
{
cipher.DoFinal(rv, tam);
}
catch (Exception ce)
{
Console.WriteLine(ce.StackTrace);
}
return rv;
}
private byte[] Decrypt(byte[] key, byte[] cipherText)
{
BufferedBlockCipher cipher = new PaddedBufferedBlockCipher(new CbcBlockCipher(engine));
cipher.Init(false, new KeyParameter(key));
byte[] rv = new byte[cipher.GetOutputSize(cipherText.Length)];
int tam = cipher.ProcessBytes(cipherText, 0, cipherText.Length, rv, 0);
try
{
cipher.DoFinal(rv, tam);
}
catch (Exception ce)
{
Console.WriteLine(ce.StackTrace);
}
return rv;
}
private static byte[] AsciiStringToBytes(string text)
{
return System.Text.Encoding.ASCII.GetBytes(text);
}
private static string BytesToAsciiString(byte[] bytes)
{
return System.Text.Encoding.ASCII.GetString(bytes);
}
}