Опубликован: 11.05.2007 | Доступ: свободный | Студентов: 1698 / 235 | Оценка: 4.36 / 4.25 | Длительность: 16:06:00
Лекция 10:

Промежуточная среда .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.