Россия, г. Москва |
Промежуточная среда .NET Remoting
В качестве примера модификации рассмотрим добавление шифрования в стандартный канал на основе симметричного шифрования. Симметричное шифрования выбрано в данном примере исключительно из за простоты реализации. На практике следовало бы скорее использовать для данной цели шифрование с открытом ключом, однако действительно правильным решением обеспечения безопасности передачи данных в Remoting заключается либо в использовании IIS, либо в развертывании VPN на основе безопасного решения (например, OpenVPN). Поэтому данную модификацию канала следует рассматривать исключительно как пример по модернизации инфраструктуры .NET Remoting.
Для реализации шифрования достаточно добавить в цепочку поставщиков труб потока дополнительного поставщика, который будет создавать трубу, шифрующую и дешифрующую проходящие по ней потоки с сериализованными сообщениями. Для добавления такого поставщика в канал следует использовать файл конфигурации. Для использовании стандартного канала с дополнительной трубой достаточно указать имя класса поставщика трубы в разделе <channel><clientProviders><provider>.
В нижеследующем файле описаны классы поставщика трубы клиента и самой трубы, а также поставщика трубы сервера и его трубы. Сам механизм шифрования на основе стандартных классов FCL описан в "Применение промежуточных сред" .
// Файл SevaRemotingEncrypted.cs using System; using System.IO; using System.Collections; using System.Runtime.Remoting.Channels; using System.Runtime.Remoting.Channels.Tcp; using System.Runtime.Remoting.Messaging; using Seva.Security.Encryption; namespace Seva.Remoting.Encryption { class EncryptedClientChannelSinkProvider: IClientChannelSinkProvider { private IClientChannelSinkProvider next; public IClientChannelSinkProvider Next { get { return next; } set { next = value; } } private SymmetricEncryptor encryptor;
Конструктор поставщика трубы на стороне клиента получает в качестве аргумента словарь свойств, заполненный свойствами из файла конфигурации. Это позволяет указывать имя файла с ключом шифрования в файле конфигурации, а в конструкторе создается симметричный шифровальщик с указанным ключом.
public EncryptedClientChannelSinkProvider(IDictionary properties, ICollection providerData) { string keyFile = (string) properties["key"]; Console.WriteLine("Client key: [{0}]", keyFile); encryptor = new SymmetricEncryptor(keyFile); }
Метод CreateSink является основным методом поставщика трубы. В типичном случае сначала вызывается этот же метод для следующего в цепочке поставщика, а затем создается труба, вставляемая в цепочку труб. Следует отметить, что данный поставщик не может быть последним в цепочке, но соответствующие проверки свойства Next и выбросы исключений для экономии места не показаны.
public IClientChannelSink CreateSink(IChannelSender channel, string url, object remoteChannelData) { IClientChannelSink next = Next.CreateSink(channel, url, remoteChannelData); return new EncryptedClientChannelSink(encryptor, next); } }
Класс поставщика трубы на стороне сервера устроен аналогичным образом.
class EncryptedServerChannelSinkProvider : IServerChannelSinkProvider { private IServerChannelSinkProvider next; private SymmetricEncryptor encryptor; public IServerChannelSinkProvider Next { get { return next; } set { next = value; } } public EncryptedServerChannelSinkProvider(IDictionary properties, ICollection providerData) { string keyFile = (string) properties["key"]; Console.WriteLine("Server key: [{0}]", keyFile); encryptor = new SymmetricEncryptor(keyFile); } // Создание трубы канала public IServerChannelSink CreateSink(IChannelReceiver channel) { IServerChannelSink nextSink = Next.CreateSink(channel); return new EncryptedServerChannelSink(channel, encryptor, nextSink); } // Обязательный метод интерфейса public void GetChannelData(IChannelDataStore channelData) { } }Листинг 8.1.
Собственно шифрование выполняется в созданными поставщиками трубах канала на стороне клиента и сервера.
class EncryptedServerChannelSink : IServerChannelSink { public IDictionary Properties { get { return null; } } private IServerChannelSink nextSink; public IServerChannelSink NextChannelSink { get { return nextSink; } } private SymmetricEncryptor encryptor; public EncryptedServerChannelSink(IChannelReceiver channel, SymmetricEncryptor channelEncryptor, IServerChannelSink next) { encryptor = channelEncryptor; nextSink = next; }
Главным методом трубы при синхронном удаленном вызове является метод ProcessMessage. В случае трубы потока данный метод может оперировать как с самим сообщением, так и с полученным из него в ходе сериализации потоком. Труба шифрования сервера должна дешифровать этот поток при получении сообщения от клиента и зашифровать поток при посылке клиенту ответа сервера.
// Метод синхронной обработки сообщения public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream) { Stream plainStream = null; MemoryStream decodedStream = new MemoryStream(); requestStream = Utils.ReadAllStream(requestStream); // Расшифровать запрос клиента encryptor.Decrypt(requestStream, decodedStream); decodedStream.Position = 0; ServerProcessing result = nextSink.ProcessMessage(sinkStack, requestMsg, requestHeaders, decodedStream, out responseMsg, out responseHeaders, out plainStream); // Зашифровать ответ сервера responseStream = new MemoryStream(); encryptor.Encrypt(plainStream, responseStream); responseStream.Position = 0; // return result; } public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers, Stream stream) { throw new NotSupportedException(); } public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, Object state, IMessage msg, ITransportHeaders headers) { return nextSink.GetResponseStream(sinkStack, state, msg, headers); } } // EncryptedServerChannelSinkЛистинг 8.2.