Created
April 25, 2013 06:43
-
-
Save DevJohnC/5457950 to your computer and use it in GitHub Desktop.
EncryptedClient that wraps another IClient
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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