Skip to content

Instantly share code, notes, and snippets.

@DevJohnC
Created April 25, 2013 06:43
Show Gist options
  • Select an option

  • Save DevJohnC/5457950 to your computer and use it in GitHub Desktop.

Select an option

Save DevJohnC/5457950 to your computer and use it in GitHub Desktop.
EncryptedClient that wraps another IClient
using System;
using FragLabs.Adjutant.Network.Interfaces;
namespace FragLabs.Adjutant.Network.Crypto
{
public class EncryptedClient : MarshalByRefObject, IClient
{
private readonly IClient _baseClient;
private readonly ISecurityCredentials _credentials;
private readonly ICredentialsVerifier _verifier;
private ICryptoProvider _verificationCryptoProvider;
private ICryptoProvider _cryptoProvider;
private ISecurityStatus _clientStatus;
public override object InitializeLifetimeService()
{
return null;
}
public event EventHandler<ClientEventArgs> ConnectComplete;
protected virtual void OnConnectComplete(ClientEventArgs e)
{
var handler = ConnectComplete;
if (handler != null) handler(this, e);
}
public event EventHandler<ClientEventArgs> Disconnected;
protected virtual void OnDisconnected(ClientEventArgs e)
{
var handler = Disconnected;
if (handler != null) handler(this, e);
}
public event EventHandler<ClientEventArgs> MessageReceived;
protected virtual void OnMessageReceived(ClientEventArgs e)
{
var handler = MessageReceived;
if (handler != null) handler(this, e);
}
public event EventHandler<ClientEventArgs> MessageReceivedRaw;
protected virtual void OnMessageReceivedRaw(ClientEventArgs e)
{
var handler = MessageReceivedRaw;
if (handler != null) handler(this, e);
}
public EncryptedClient(IClient baseClient, ISecurityCredentials credentials = null, ICredentialsVerifier verifier = null)
{
if (baseClient == null) throw new ArgumentNullException("baseClient");
_baseClient = baseClient;
_credentials = credentials;
_verifier = verifier;
_baseClient.ConnectComplete += (sender, args) => OnConnectComplete(new ClientEventArgs(this, args.OperationSuccessful));
_baseClient.Disconnected += (sender, args) => OnDisconnected(new ClientEventArgs(this, args.OperationSuccessful));
_baseClient.MessageReceived += (sender, args) =>
{
var msg = DecryptMessage(args.Message);
if (msg is NegotiateMessage)
{
switch ((EncryptedMessageType) msg.Words[0][0])
{
case EncryptedMessageType.RsaFingerprint:
msg.IsResponse = true;
msg.Words = new[]
{
new[] { (byte)EncryptedMessageType.RsaFingerprint },
_credentials.Fingerprint
};
Send(msg);
break;
case EncryptedMessageType.RsaCryptoCheck:
msg.IsResponse = true;
msg.Words = new[]
{
new[] { (byte)EncryptedMessageType.RsaCryptoCheck },
_credentials.LocalCryptoProvider.Decrypt(msg.Words[1])
};
Send(msg);
break;
case EncryptedMessageType.CryptoKeys:
_cryptoProvider = new RijndaelProvider(msg.Words[1], msg.Words[2]);
msg.IsResponse = true;
msg.Words = new[]
{
new[] { (byte)EncryptedMessageType.CryptoKeys }
};
Send(msg);
break;
case EncryptedMessageType.Failure:
throw new Exception(String.Format("Failed to negotiate encryption, {0}", System.Text.Encoding.UTF8.GetString(msg.Words[1])));
}
}
else
{
OnMessageReceived(new ClientEventArgs(this, args.OperationSuccessful, msg));
OnMessageReceivedRaw(new ClientEventArgs(this, args.OperationSuccessful, msg));
}
};
}
/// <summary>
/// Negotiates crypto protocols.
/// </summary>
public void Negotiate(Action<bool> callback = null)
{
// send request for RSA public key signature
Send(new NegotiateMessage
{
IsFromServer = true,
IsResponse = false,
Words = new[] { new[] { (byte)EncryptedMessageType.RsaFingerprint } }
}, (client, message) => NegotiateRsaFingerprintResponse(client, message, callback));
}
private void NegotiateRsaFingerprintResponse(IClient client, Message message, Action<bool> callback)
{
// verify received signature
var securityState = _verifier.VerifyFingerprint(client, message.Words[1]);
if (securityState == null)
{
// failure
message.Words = new[] { new[] { (byte)EncryptedMessageType.Failure }, System.Text.Encoding.UTF8.GetBytes("Fingerprint not valid") };
message.IsResponse = true;
Send(message);
if (callback != null)
callback(false);
}
else
{
_clientStatus = securityState;
// encrypt some random data with client's public key and send it
_verificationCryptoProvider = _verifier.GetCryptoProvider(client, message.Words[1]);
byte[] expectedData;
var verifyData = _verifier.GenerateVerificationBytes(_verificationCryptoProvider, out expectedData);
Send(new NegotiateMessage
{
IsFromServer = true,
IsResponse = false,
Words = new[] { new[] { (byte)EncryptedMessageType.RsaCryptoCheck }, verifyData }
}, (client1, message1) => NegotiateRsaCryptoCheckResponse(client1, message1, expectedData, callback));
}
}
private void NegotiateRsaCryptoCheckResponse(IClient client, Message message, byte[] expectedData, Action<bool> callback)
{
var responseValid = _verifier.VerifyVerificationBytes(_verificationCryptoProvider, expectedData, message.Words[1]);
if (!responseValid)
{
// failure
message.Words = new[] { new[] { (byte)EncryptedMessageType.Failure }, System.Text.Encoding.UTF8.GetBytes("Verification data invalid") };
message.IsResponse = true;
Send(message);
if (callback != null)
callback(false);
}
else
{
_cryptoProvider = new RijndaelProvider();
var iv = _cryptoProvider.GenerateIV();
var key = _cryptoProvider.GenerateKey();
Send(new NegotiateMessage
{
IsFromServer = true,
IsResponse = false,
Words = new[] {new[] {(byte) EncryptedMessageType.CryptoKeys}, iv, key}
}, (client1, message1) =>
{
if (callback != null)
callback(true);
});
}
}
/// <summary>
/// Gets the client's security status.
/// </summary>
public ISecurityStatus ClientStatus
{
get { return _clientStatus; }
}
/// <summary>
/// Gets or sets the cryptography provider for the client.
/// </summary>
public ICryptoProvider CryptoProvider
{
get { return _cryptoProvider; }
set { _cryptoProvider = value; }
}
public void Connect()
{
_baseClient.Connect();
}
public void Disconnect()
{
_baseClient.Disconnect();
}
public bool IsConnected
{
get { return _baseClient.IsConnected; }
}
public void Send(Message message)
{
_baseClient.Send(EncryptMessage(message));
}
public void Send(Message message, Action<IClient, Message> responseCallback)
{
_baseClient.Send(EncryptMessage(message), (client, message1) =>
{
var msg = DecryptMessage(message1);
responseCallback(this, msg);
OnMessageReceivedRaw(new ClientEventArgs(this, true, msg));
});
}
private Message EncryptMessage(Message message)
{
if (message is NegotiateMessage)
{
var ret = new Message
{
IsFromServer = message.IsFromServer,
IsResponse = message.IsResponse,
MessageId = message.MessageId,
Words = new[]
{
new[] { (byte)EncryptedMessageType.Negotiate },
message.Encode()
}
};
return ret;
}
else if (_cryptoProvider == null)
{
var ret = new Message
{
IsFromServer = message.IsFromServer,
IsResponse = message.IsResponse,
MessageId = message.MessageId,
Words = new[]
{
new[] { (byte)EncryptedMessageType.NotEncrypted },
message.Encode()
}
};
return ret;
}
else
{
var ret = new Message
{
IsFromServer = message.IsFromServer,
IsResponse = message.IsResponse,
MessageId = message.MessageId,
Words = new[]
{
new[] { (byte)EncryptedMessageType.Encrypted },
_cryptoProvider.Encrypt(message.Encode())
}
};
return ret;
}
}
private Message DecryptMessage(Message message)
{
if (message.Words.Length != 2) return null;
if (message.Words[0][0] == (byte) EncryptedMessageType.NotEncrypted)
{
var ret = Message.Decode(message.Words[1]);
ret.MessageId = message.MessageId;
ret.IsFromServer = message.IsFromServer;
ret.IsResponse = message.IsResponse;
return ret;
}
if (message.Words[0][0] == (byte)EncryptedMessageType.Negotiate)
{
var ret = NegotiateMessage.Decode(message.Words[1]);
ret.MessageId = message.MessageId;
ret.IsFromServer = message.IsFromServer;
ret.IsResponse = message.IsResponse;
return ret;
}
else if (message.Words[0][0] == (byte) EncryptedMessageType.Encrypted)
{
var ret = Message.Decode(_cryptoProvider.Decrypt(message.Words[1]));
ret.MessageId = message.MessageId;
ret.IsFromServer = message.IsFromServer;
ret.IsResponse = message.IsResponse;
return ret;
}
return null;
}
public System.Net.IPAddress RemoteIP
{
get { return _baseClient.RemoteIP; }
}
public void Dispose()
{
_baseClient.Dispose();
}
}
internal enum EncryptedMessageType : byte
{
NotEncrypted = 1,
Encrypted = 2,
Negotiate = 3,
RsaFingerprint = 120,
RsaCryptoCheck = 121,
CryptoKeys = 122,
Failure = 123
}
internal class NegotiateMessage : Message
{
public static NegotiateMessage Decode(byte[] encodedMessage)
{
var original = Message.Decode(encodedMessage);
var ret = new NegotiateMessage
{
Words = original.Words,
IsFromServer = original.IsFromServer,
IsResponse = original.IsResponse,
MessageId = original.MessageId
};
return ret;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment