Основные цели построения прототипа - научиться:
строить много проектные решения (много файловые сборки) в
среде Visual Studio/C# (приложения, состоящие из одного .exe и
нескольких .dll);
строить программные системы на основе нескольких программных
приложений, взаимодействующих как в среде отдельного компьютера, так
и в рамках локальной сети и Internet;
удаленно работать с конфиденциальной информацией SQL сервера,
используя в качестве транспорта данных Интернет или незащищенный
сегмент локальной сети,
защищать клиентское приложение от несанкционированной
модификации,
а также, определить и осуществить реализацию минимального состава
необходимых методов хеширования, сериализации типов, компрессии и
шифрования передаваемой информации.
В процессе построения прототипа решены следующие задачи:
- аутентификации пользователя, инициирующего обмен;
- авторизации пользователя в процессе работы;
- ограничения доступа к функциям и записям таблиц базы данных
информационной системы (управление доступом);
- защиты трафика;
- страничного представления информации клиенту;
- компрессии информации;
- защиты клиентского приложения от несанкционированной
модификации;
- адаптации структуры системы к информационной нагрузке,
а также:
- осуществлена реализация необходимых программных методов;
- построено много проектное решение для пользовательского
интерфейса,
- построена программная система, состоящая из пяти
взаимодействующих приложений на основе .Net Remoting (клиентское
приложение, сервис взаимодействия с клиентом, сервис хранения
запросов/ответов, КриптоСервер, база данных на SQL сервере),
- разработан и реализован общий подход к настройке WEB-, .NET-
сервисов и Windows приложений.
Прибавления и изменения по отношению к первой редакции:
- Не рассматриваются и не упоминаются системы на основе ASP.Net и
Web сервисов. Развитие прототипа осуществляется только на базе
Remote .Net сервисов поверх IIS или Remote .Net сервера
- Кратко оттенены преимущества построения информационных систем на
основе взаимодействующих Remote .Net сервисов
- Подробно рассмотрен измененный процесс аутентификации клиента
- Описан процесс взаимодействия клиентского singlecall сервиса с
системой динамических абонентских ящиков на основе событийной модели
- Рассказано о новом алгоритме построения страницы результирующего
набора данных
- Приведена схема защиты клиентского приложения от
несанкционированной модификации.
Особенности прототипа:
- клиентское приложение прототипа свободно распространяется в
Интернете, функционирует в незащищённой среде и защищено от
несанкционированной модификации;
- прототип не использует для клиентов учётные записи локальной
сети Windows.
Построение информационной системы на основе взаимодействующих .Net
Remoting сервисов имеет определенное преимущество, - связь в сети одного
сервиса к другим осуществляется по адресам URL формата (например:
tcp://alpha:5000/ или http://localhost:80/).
Это даёт возможность отладить все компоненты системы, используя только
один компьютер. И создать на их основе реальную систему такую, как на
показано рисунке. Для адаптации информационной системы к реальным
нагрузкам можно, достаточно произвольно, менять структуру компьютерной
составляющей. Например, если система для адекватной реакции на нагрузку,
должна иметь несколько IIS серверов на разных компьютерах, то
проектировщик добавит в схему дополнительно нужное число компьютеров для
размещения серверов хранения запросов/ответов и т.п. Собираем нужную
программно-техническую структуру системы из "кубиков".
Прототип содержит три базовых компонента (схемотехнических элемента,
"кубика") - КриптоСервер (сервер бизнес-логики), Remote .Net сервер
хранения запросов/ответов на базе консольного приложения и сервер
управления взаимодействием с удаленными клиентами. В качестве связующих
стрелок используются адреса URL. К одному базовому схемотехническому
элементу - серверу хранения запросов/ответов - можно с одной стороны
подключить несколько IIS элементов c Remote .Net сервисами, а с другой
несколько других элементов - серверов бизнес-логики, и получить
вычислительный узел. Меняя число и состав вычислительных узлов,
решаем задачу адаптации структуры системы к информационной нагрузке.
Программный комплекс из двух элементов - сервера управления
взаимодействием с удаленными клиентами и Remote .Net сервера хранения
запросов/ответов - серьёзный инструмент для создания распределенных
систем. В абонентский ящик можно положить, как запрос клиента на
выполнение работы сервером, так и сообщение клиента о готовности
выполнить работу, которую закажет сервер.
Построение информационной системы на основе взаимодействующих .Net
Remoting сервисов целесообразно также и экономически - клиентские
лицензии для работы с базой данных (например, Oracle) требуется
установить только на компьютеры c КриптоСервер-ами. Так для 1000
клиентов достаточно двух лицензий. И ещё один интересный момент -
достаточно всего одной! дополнительной учетной записи клиента, от имени
которого работаем с базой данных и запускаем КриптоСервер-а. Клиент не
работает напрямую с базой данных.
Построенный прототип реально представляет собой распределённую
систему управления централизованным каталогом видео фильмов.
Самостоятельно может работать в студенческих общежитиях или локальной
сети квартала. Как прототип может служить базой для построения
распределённых информационных систем в банковском деле (удаленный АРМ
операционного дня, АРМ клиента банка), построения общегородских
распределённых информационных систем по наличию лекарств в аптеках,
построения информационных систем супермаркетов, товарных складов и т.п.
Аутентификация пользователя и построение сессии.
Каждый пользователь защищенной информационной системы
идентифицируется уникальной учетной записью, которая физически является
строкой таблицы Владельцы базы данных информационного SQL сервера.
Структура учетной записи
N/N |
Name |
Data Type |
Size |
1 |
GUID |
uniqueidentifier |
16 |
2 |
Имя |
nvarchar |
64 |
3 |
hshLogin |
binary |
20 |
4 |
hshPassword |
binary |
20 |
5 |
Cmd |
int |
4 |
6 |
e_mail |
nvarchar |
64 |
7 |
hshPinCod |
binary |
20 |
8 |
kiPinCod |
binary |
32 |
3, 4 и 7 поле строки содержат hash представления Login, Password и
PinCod-а пользователя.
5 поле (cmd) содержит битовый вектор доступа к функциям и объектам
системы. Поле 8 содержит параметры шифрования файла на клиентской
машине, содержащего hshLogin и hshPassword.
Учетные записи предопределённых пользователей защищенной
информационной системы - Администратора и Гостя, генерируются при
первоначальном запуске Windows .Net приложения "КриптоСистема" на любом
из КриптоСерверов. Учетные записи других пользователей создаются
Администратором.
Администратор системы, используя Windows. Net приложение
"RSAKeyPairGen.exe", генерирует также и ключевую пару ассимметричного
криптографического алгоритма. Открытый ключ, вместе с клиентским
Windows.Net приложением "wсКаталогВидео", передается пользователю.
Предопределённый пользователь может получить доступ к данным или как
Гость, с минимальными возможностями (только просмотр имеющихся в наличии
фильмов), или как Администратор, с почти неограниченными возможностями,
но для этого ему надо знать Login и Password. Гость и Администратор не
имеют PinCod-а. Кроме группы предопределённых пользователей существует
группа пользователей, являющихся владельцами фильмов. Для своего фильма
владелец может редактировать все параметры. Для совместного фильма -
только ограниченное число своих параметров.
Запуская клиентское приложение на своей рабочей станции, пользователь
любой группы попадает в состояние регистрации. Его просят ввести
аутентификационную информацию - Login и Password или PinCod. Информация
вводится стандартным защищенным способом с использованием DPAPI-функция
CredUIPromptForCredentials. Реализованы два варианта аутентификации
пользователя:
В дальнейшем, в байтовых последовательностях будем выделять
фрагменты, - без шифрования (…), шифрованные открытым ключем {…},
шифрованные симметричным ключем […].
Вариант 1. Пользователь вводит информации в поля Login и
Password. Клиентское приложение создаёт временные код сессии (КС), ключ
(Key) и initialization vector (IV) симметричного алгоритма шифрования.
Последовательность запроса = (КС +IV+0x00)+{Key}+[0x0003] передаётся
КриптоСерверу для получения "временного параметра" ВП. По его получению
программа генерирует постоянные КС и Key сессии, и временный IV. Для
Login и Password строятся их hash представления, к ним добавляется
"временной пар аметр", которые вместе шифруются симметричным ключом;
симметричный ключ шифруется открытым ключом ассимметричного алгоритма.
Байтовая последовательность = (КС+IV+0x00)+ {Key}+ [0x0001+ ВП+ hash
Login+hash Password] передается КриптоСерверу.
Вариант 2. Пользователь вводит информацию только в поле Password
(PinCod). Клиентское приложение создаёт временные КС, Key, IV, строит
байтовую последовательность запроса "временного параметра" = (КС
+IV+0x00)+{Key}+[0x0003] и передаёт её КриптоСерверу. После получения ВП
от КриптоСервера программа строит временные КС, Key, IV и hash
представление PinCod-а, добавляет к ним "временной параметр", шифрует и
делает запрос КриптоСерверу на предмет наличия зарегистрированного
пользователя с таким PinCod-ом. Если сервер подтверждает наличие,
передавая Key и IV шифрования, то программа считывает и дешифрирует hash
представления Login и Password из файла hshLP.bin и далее аналогично
первому варианту. Расположение файла hshLP.bin задается в файле
настройки - или на съемном носителе (дискета, cd, флеш) или крипто
защищённом личном разделе жесткого диска.
Схема передачи запроса КриптоСерверу: клиентское приложение ->
сервер взаимодействия с клиентом -> сервер хранения запросов/ответов
->КриптоСервер
- сервер взаимодействия с клиентом это Remote .Net сервер
"crService_KatalogVideo" на базе консольного приложения - для клиентов
локальной сети, IIS - для клиентов Интернета. Сервер взаимодействия с
клиентом обеспечивает функционирование Remote .Net singlecall сервисов
"сrService_KatalogVideo_Lib" управления взаимодействием с клиентом.
Информационные запросы клиентов динамически порождают создание копий
singlecall сервиса в среде Remote .Net сервера или IIS. Для каждого
клиента - своя копия. Как только ответ будет передан клиенту, его копия
singlecall сервиса уничтожается.
- сервер хранения запросов/ответов это Remote .Net сервер
"rServer_УправлениеЗапросОтвет" на базе консольного приложения. Сервер
обеспечивает функционирование Remote .Net singleton сервиса управления
системой динамических абонентских ящиков.
Сервисы взаимодействия с клиентом и управления системой динамических
абонентских ящиков никаких крипто преобразований над байтовыми
последовательностями запроса/ответа не осуществляют. Они создают канал в
Интернете (сегменте локальной подсети), по которому передаётся
шифрованная информация.
Взаимодействие Remote .Net сервисов с другими приложениями
реализовано на основе интерфейсов ими представляемых, например, для
сервиса управления системой динамических абонентских ящиков
public delegate void dlgEventHandlerОтвет(short j);
public interface IКлиент {
short БронированиеАбонентскогоЯщика(byte[] gd);
void ПоложитьЗапросВЯщик(short idx,byte[] bv,dlgEventHandlerОтвет vtHandler);
void ВзятьОтветИзЯщика(short idx,byte[] gd);
}
Реализация ожидания и обработки события ответа на запрос в сервисе
взаимодействия с клиентом
public class crServer_KatalogVideo_Impl: MarshalByRefObject,IОбработкаЗапроса,IКлиент
{
static AutoResetEvent[] evtОжидание = new AutoResetEvent[1000];
...
//-- Функция обработки ответа на запрос клиента
//-- вызывается, как только ответ помещен
//-- в абонентский ящик клиента
[OneWay]
public void fОтвет(short j) {
if(j>=0){
//-- Продолжаем обработку запроса данным сервисом.
if(!(evtОжидание[j]==null)) ((AutoResetEvent)evtОжидание[j]).Set();
}
}
...
//-- Пытаемся бронировать динамический абонентский ящик
for(i=10;i>=0;i--) { //-- Ждем освобождения абонентского ящика
idx=iКлиент.БронированиеАбонентскогоЯщика(gd);
if(idx>=0) break;
Thread.Sleep(2000);
}
if(idx<0) return new byte[2]{1,2}; //-- Ошибка записи строки запроса
//-- создаём объект для ожидания
evtОжидание[idx]=new AutoResetEvent(false);
//-- В метод удаленного сервиса передается делегат обработчика события
iКлиент.ПоложитьЗапросВЯщик(idx,bv,new dlgEventHandlerОтвет(fОтвет));
//-- Ожидаем байтовую строку ответа
if(evtОжидание[idx].WaitOne(iTimeOut,false)) //-- не допустим вечный цикл
{//-- Не time-out. Запрос обработан
evtОжидание[idx].Reset();
evtОжидание[idx]=null;
return(iКлиент.ВзятьОтветИзЯщика(idx,gd)); //-- Сериализованный ответ
}
else
{//-- Time-out
return new byte[2]{1,2}; //-- Ошибка записи строки запроса
}
...
Сервис управления системой динамических абонентских ящиков получает
байтовую последовательность запроса от клиентского приложения, проводит
её частичный анализ, бронирует абонентский ящик, помещает в него
байтовую строку запроса и переходит в состояние ожидания ответа.
Сервис хранит информацию о запросах/ответах в следующих структурах:
byte[] даяКодСостояния |
значения компоненты:
0xff-бронировано, 0x00-свободно, 0x01-запрос, 0x02-запрос
обрабатывается; 0x03-запрос обработан. |
Guid[] даяСессия |
компонента хранит Guid сессии |
object[] даяЗапросОтвет |
компонента хранит
сериализованное, сжатое и шифрованное представление
запроса/ответа |
DateTime[] даяДлительность |
компонента хранит максимально
допустимое время обработки запроса (длительность бронирования
сессией динамического абонентского ящика) |
dlgEventHandlerОтвет[] даяОтвет |
компонента хранит информацию,
необходимую для вызова функции обработки ответа сервиса
"сrService_KatalogVideo_Lib" по завершению обработки запроса.
|
Совокупность компонент разных векторов с одним индексом образуют
динамический почтовый ящик (префикс "дая").
- КриптоСервер это Windows .Net приложение, взаимодействующее
с сервисом управления системой динамических абонентских ящиков
посредством Remote .Net технологии. Все решения принимаются и
выполняются только на уровне КриптоСерверов. КриптоСервера
являются "мозгом" всей пяти уровневой системы. Каждый КриптоСервер
постоянно сканирует (точнее, обращается в цикле к сервису хранения
запросов/ответов) вектор даяКодСостояния[] сервиса хранения
запросов/ответов и при наличии запроса переходит к его детальному
анализу и выполнению. Если запрос может быть обработан данным
КриптоСервером, то анализ продолжается, иначе работа над запросом
завершается без уничтожения запроса. Запросы разделены на группы по
признаку максимальной длительности выполнения и есть серверы,
выполняющие только короткие запросы.
Если пользователь с данными Login и Password создан (имеется
соответствующая запись в таблице Владельцы информационного SQL сервера),
то генерируется запись в таблицу сессий информационного SQL сервера и
запись в структуры сервиса управления системой динамических абонентских
ящиков и КриптоСервер продолжает сканирование. Группа функций с кодом
0x00 отвечает за регистрацию клиента. Любой пользователь может
активизировать несколько самостоятельных сессий.
Структура записи таблицы сессий
N/N |
Name |
Data Type |
Size |
Примечание |
1 |
gdСессия |
uniqueidentifier |
16 |
Код (guid) сессии |
2 |
gdКлиент |
uniqueidentifier |
16 |
Код (guid) клиента (владельца),
связь с таблицей Владельцы |
3 |
dtСессия |
datetime |
8 |
Time-out |
4 |
tdsKey |
binary |
24 |
Ключ симметричного шифрования
|
5 |
Cmd |
int |
4 |
Битовый вектор доступа |
6 |
ivKey |
binary |
8 |
IV симметричного алгоритма
шифрования |
7 |
strЗапрос |
ntext |
16 |
Храним SELECT запроса фильмов |
8 |
hs |
image |
16 |
Храним сериализованный список
Guid |
По завершению обработки запроса КриптоСервер записывает ответ в
компоненту вектора даяЗапросОтвет[] и устанавливает компоненту
даяКодСостояния[] =0x03. Сервис хранения запросов/ответов, используя
переданный делегат, активизирует функцию fОтвет обработки события в
сервисе взаимодействия с клиентом. Функция толкает застывший клиентский
сервис, который забирает ответ из динамического абонентского ящика и
отправляет его байтовую строку через Интернет (сегмент локальной
подсети) клиенту. Сервис прекращает своё существование.
Структура байтовой последовательности ответа в случае регистрации
клиента
Ошибка - (двухбайтовый код ошибки)
Штатно - (0x00+0x00+IV)+[симметрично зашифрованное Info]
Структура Info ответа для регистрации:
[cmd клиента]+[guid аутентифицированного клиента]+[размер
страницы]+[имя клиента]
Структура байтовой последовательности для общего случая
информационного запроса/ответа:
Запрос:
(КС+IV+индекс группы команд)+[Info]
Структура Info запроса:
[индекс команды в группе+параметры команды]
Структура ответа:
Ошибка - (двухбайтовый код ошибки)
Штатно - (0x00+0x00+IV)+[Info]
В общем случае Info ответа содержит результирующий набор данных в
виде строк отсортированного массива объектов. Размер массива может
превышать допустимые границы, например, в случае запроса показать все
фильмы. Для работы с подобными наборами данных используем страницы. Идея
проста - результирующий набор данных делится на страницы и за единичный
акт обмена на рабочую станцию пользователя передаётся одна страница
отсортированного результирующего набора данных. С целью уменьшения
трафика, предложение SELECT для построения страничного набора данных и
его параметры передаются по сети единожды и хранятся в полях строки
таблицы Сессий (strЗапрос, hs). Для получения следующей страницы
достаточно передачи её номера. Размер страницы в записях - параметр
настройки сервиса управления системой динамических абонентских ящиков.
По отдельному запросу величину размера получают КриптоСервера. Основную
часть задачи страничного представления информации клиенту решает
SQL сервер двумя динамическими запросами:
CREATE PROCEDURE au_getPageФильмыDel
@Sel_1 ntext,
@RowNum int,
@Sel_2 ntext
AS
-- очистим таблицы GUID Фильмов
TRUNCATE TABLE #gdФильмы
TRUNCATE TABLE #gdTmp
-- Запись отсортированных ключей в #gdTmp
EXECUTE(@Sel_1)
-- Перепись суррогатных ключей страницы
INSERT INTO #gdФильмы(GUID)
SELECT GUID FROM #gdTmp WHERE RowNum > @RowNum ORDER BY RowNum"
TRUNCATE TABLE #gdTmp
-- Вот эти данные уходят на клиента
EXECUTE(@Sel_2)
GO
Каждый "КриптоСервер" имеет один Connect с базой данных. В рамках
Connect-а строятся две временные таблицы - #gdФильмы и #gdTmp
CREATE TABLE #gdФильмы (RowNum int IDENTITY,[GUID] [uniqueidentifier]);
CREATE TABLE #gdTmp (RowNum int IDENTITY,[GUID] [uniqueidentifier])
Первым динамическим запросом "режем" таблицу по длине и заполняем
временную таблицу #gdTmp значениями суррогатных ключей выбранных
отсортированных строк исходной таблицы
INSERT INTO #gdTmp(GUID) SELECT TOP ... GUID FROM Фильмы WHERE ... ORDER BY ...
Вторым динамическим запросом "режем" выбранные строки таблицы по
ширине и возвращаем выборку
SELECT f.GUID, f.Преф, f.Название_рус ...
FROM Фильмы f INNER JOIN #gdФильмы g ON f.GUID=g.GUID ORDER BY g.RowNum
Фрагмент программы получения страницы:
...
//-- Получим страницу выборки по фильмам
sscФильмы.Parameters.Clear();
sscФильмы.Parameters.Add("@Sel_1", SqlDbType.NText).Value = Sel_1;
sscФильмы.Parameters.Add("@RowNum ", SqlDbType.Int).Value = RowNum;
sscФильмы.Parameters.Add("@Sel_2", SqlDbType.NText).Value = Sel_2;
sscФильмы.CommandText = "au_getPageФильмыDel";
sscФильмы.CommandType = CommandType.StoredProcedure;
dsКриптоСистема.Фильмы.Clear();
sdaФильмы.Fill(dsКриптоСистема,"Фильмы");
dsКриптоСистема.Фильмы.AcceptChanges();
В процессе формирования ответа страница обрабатывается Zip
компрессором данных. Привожу фрагмент программы сериализации и
компрессии страницы:
//-- Построим вектор строк из таблицы результата
int max=dsКриптоСистема.Фильмы.Count;
object[]objArray=new Object[max];
//-- objArray страница
for(int i=0;i<max;i++) objArray[i]=dsКриптоСистема.Фильмы.Rows[i].ItemArray;
//-- Сериализация и компрессия
if(!binSerObject(objArray)) {. . .} //-- сериализация
if(!Компрессор()) {. . . } //-- компрессия
Авторизация пользователя при дальнейших запросах производится по
коду сессии и параметрам симметричного алгоритма шифрования (ключу и IV
шифра).
Ограничения доступа пользователя к функциям и записям таблиц
базы данных информационной защищенной системы задаются битовым вектором
"cmd". Решение по ограничению доступа принимаются КриптоСервером
(уровень бизнес-логики).
Защита трафика осуществляется путем шифрования симметричным
ключом сессии передаваемой байтовой последовательности.
Для защиты передачи симметричного ключа сессии используется открытый
ключ
Защита клиентского приложения от несанкционированной модификации
реализована так:
- клиентское приложение создано в форме много проектного решения и
компилируется с использованием строгих имен,
- его узловая сборка (файл clsStartCrypto.dll) зашифрована в файл
clsStartCrypto.cry,
- если клиент идентифицирован, то КриптоСервер передаёт
клиентскому приложению параметры дешифрования файла
clsStartCrypto.cry,
- клиентское приложение дешифрирует файл, загружает сборку,
создает и активизирует объект заданного класса (допустим и вариант
пересылки сборки КриптоСервером на клиентский компьютер):
. . .
//-- Дешифруем clStartSer.cry (крипто clStartSer.dll)
//-- ivKey - параметры дешифрования, переданные КриптоСервер-ом
//-- xbb <-- clStartSer.dll (byte[]xbb - переменная уровня класса)
if(!TDESDecryptStartCrypto(ivKey)) return false;
Assembly asStartSer=null;
object obj=null;
try {
//-- Загружаем сборку
asStartSer=Assembly.Load(xbb);
Type[] mytypes = asStartSer.GetTypes();
obj = Activator.CreateInstance(mytypes[0],null,null);
return true;
}
catch
{return false;}
Сборка clsStartCrypto.dll состоит из одного класса. Для
доступа к его методам используются делегаты. Сборка должна
компилироваться в процессе компиляции всего приложения, но не один
элемент приложения не должен напрямую ссылаться на сборку
clsStartCrypto.dll (не должен, используя new,
создавать объект из класса, содержащегося в сборке).
Производительность информационной системы в широком диапазоне
регулируется изменением числа "КриптоСерверов" на вычислительном
узле и изменением числа вычислительных узлов. Попытка кардинально
повысить производительность системы за счет ручной сериализации
результирующего набора строк таблицы фильмов оказалась не состоятельной.
Внутренние методы сериализации .Net Framework работают прекрасно и
разумно пользоваться именно ими.
Реализация программных методов хеширования, сериализации
типов, компрессии информации и шифрования
//-- Переменные уровня класса
byte[] xbb;
MemoryStream xms;
byte[] TDESKey=byte[24];
byte[] TDESIV=byte[8];
int xbbN;
//-- =================
//-- == Хеширование ==
//-- =================
//-- Формируем hash код строки
//====================================
private byte[] hКод(string s){
UnicodeEncoding UE = new UnicodeEncoding();
//-- Преобразование строки в вектор байтов.
byte[] bv = UE.GetBytes(s);
//-- Создадим объект SHA1Managed для формирования hash значения.
SHA1Managed SHhash = new SHA1Managed();
//-- Построим hash значение для байтового вектора.
return SHhash.ComputeHash(bv);
}
//-- ========================
//-- == Сериализация типов ==
//-- ========================
//-- Бинарная сериализация известного объекта
//-- В нашем случае зто вектор строк результирующего набора данных
//-- xms <-- байтовое представление объекта
//=========================================================
private bool binSerObject(object obj){
try {
xms=new MemoryStream();
BinaryFormatter binForm=new BinaryFormatter();
binForm.Serialize(xms,obj,null);
xms.Position=0;
return true;
}
catch {return false;}
}
//-- Бинарная десериализация известного объекта из памяти
//-- xms <-- байтовое представление объекта
//=========================================================
private object binDeSerObject(){
try {
BinaryFormatter binForm=new BinaryFormatter();
return binForm.Deserialize(xms,null);
}
catch {return null;}
}
//-- Формируем 4-х байтовое представление int
//-- ii целое
//-- k индекс в xbb
//-- xbb целое
//=================================================
private void IntToXBB(int ii,int k) {
byte[] bt=BitConverter.GetBytes(ii);
bt.CopyTo(xbb,k);
}
//-- Формируем 4-х байтовое представление int
//-- xms целое
//=================================================
private void IntToXms(int ii) {
byte[] bt=BitConverter.GetBytes(ii);
xms.Write(bt,0,bt.Length);
}
//------------ Восстановление int ------------
//-- int байтовое представление xbb
//==========================================================
private int XBBToInt()
{return BitConverter.ToInt32(xbb,0);}
//------------ Восстановление int ------------
//-- int байтовое представление xms
//==========================================================
private int xmsToInt() {
byte[]bt=new Byte[4];
xms.Read(bt,0,4);
return BitConverter.ToInt32(bt,0);
}
//-- Формируем 8-и байтовое представление long
//-- xms long
//=================================================
private void LongToXms(long ii) {
byte[] bt=BitConverter.GetBytes(ii);
xms.Write(bt,0,bt.Length);
}
//------------ Восстановление long ------------
//-- long xms(байтовое представление long)
//==========================================================
private long xmsToLong() {
byte[]bt=new Byte[8];
xms.Read(bt,0,8);
return BitConverter.ToInt64(bt,0);
}
//-- Формируем 8-и байтовое представление DateTime в xms
//-- xms DateTime
//========================================================
private static void DateTimeToXMS(DateTime dt) {
LongToXMS(dt.Ticks);
}
//-- Восстановление DateTime из xms
//-- DateTime xms
//==========================================================
private static DateTime xmsToDateTime() {
long n=xmsToLong();
return new DateTime(n); //-- DateTime <-- long
}
//-- Работа с байтовым представлением строкового типа
//-- s - Строковый тип; bt - байтовое представление
//==========================================================
byte[]bt=UnicodeEncoding.UTF8.GetBytes(s); //-- byte[] s
string s=UnicodeEncoding.UTF8.GetString(bt); //-- s byte[]
//-- ===========================
//-- == Компрессия информации ==
//-- ===========================
//-- Компресор
//-- вход: xbb (вектор для компресии)
//-- выход: xbb (длина исходного(4)+сжатый вектор)
//=========================================================
private bool Компрессор(){
if(xbb.Length==0) return true;
byte[]bb;
try {
bb=ZipBase.Compress(xbb);
int m=xbb.Length;
xbb=new byte[4+bb.Length];
IntToXBB(m,0);
bb.CopyTo(xbb,4);
return true;
}
catch {return false;}
}
//-- Декомпрессор
//-- вход:
//-- фрагмент xms содержит компресированный байтовый вектор,
//-- указатель стоит на начале фрагмента
//-- выход;
//-- xms <-- декомпресированное Info
//===============================================================================
private bool Декомпрессор(){
byte[] bb=new byte[((int)xms.Length)-4];
try {
int n=xmsToInt(); //-- длина исходного Info
byte[] bb1=new byte[n]; //-- место для декомпрессированного Info
xms.Read(bb,0,bb.Length); //-- bb <-- компрессированное Info
ZipBase.Uncompress(bb1,bb);
xms.Close();
xms=new MemoryStream(bb1,0,bb1.Length);
return true;
}
catch {
xms.Close();
return false;
}
}
//-- ================
//-- == Шифрование ==
//-- ================
//-- TDES крипто
//-- вход: xbb (вектор для шифрования)
//-- выход: xbb (шифрованный вектор)
//=========================================================
private bool TDESEncrypt(){
if(xbb.Length==0) return true;
CryptoStream cs=null;
MemoryStream ms=null;
try {
byte[]bb=new Byte[32];
byte[]key=new byte[24];
byte[]iv=new byte[8];
new RNGCryptoServiceProvider().GetBytes(bb);
//-- байтовый вектор bb содержит хорошо рандомизированную величину
for(int i=0;i<8;i++) TDESIV[i]=bb[i];
TDESKey.CopyTo(key,0);
TDESIV.CopyTo(iv,0);
ms=new MemoryStream();
cs=new CryptoStream(ms,tdes.CreateEncryptor(key,iv),CryptoStreamMode.Write);
cs.Write(xbb,0,xbb.Length);
cs.FlushFinalBlock();
xbb=ms.ToArray();
cs.Close();
ms.Close();
return true;
}
catch {
if(cs!=null) cs.Clear();
if(ms!=null) ms.Close();
return false;
}
}
//-- TDES декрипто
//-- фрагмент MemoryStream xms содержит шифрованную байтовую последовательность,
//-- указатель стоит на начале фрагмента
//-- ln <-- длина фрагмента
//===============================================================================
private bool TDESDecrypt(int ln){
CryptoStream cs=null;
try {
byte[]key=new Byte[24];
byte[]iv=new Byte[8];
TDESKey.CopyTo(key,0);
TDESIV.CopyTo(iv,0);
cs=new CryptoStream(xms,tdes.CreateDecryptor(key,iv),CryptoStreamMode.Read);
xbbN=(int)cs.Read(xbb,0,ln);
cs.Close();
return true;
}
catch {
if(cs!=null) cs.Clear();
return false;
}
}
В качестве Web сервера используется IIS, с максимально отключенными
возможностями.
В качестве Remote .Net сервера используется консольное приложение.
В качестве ассимметричного алгоритма шифрования используется 1024
битный RSA, в качестве симметричного - TrippleDES.
Сервисы взаимодействия с клиентом и хранения запросов/ответов ни под
каким предлогом не могут войти во внутреннюю локальную сеть и получить
доступ к КриптоСерверам и данным информационного SQL сервера!!!!
Все приложения системы (клиентские, Remote сервис под Web сервер,
Remote сервис под Remote сервер, КриптоСервер и RSAKeyPairGen) написаны
на C#. Клиентское Windows.Net приложение для работы с Remote .Net
сервисом на IIS сервере или с Remote .Net сервисом на Remote .Net
сервере написано в формате много проектного решения (много файловой
сборки).
Что должно быть в промышленной версии, но отсутствует в прототипе:
- проработка вопросов сопровождения ПО, включая upgrate
клиентской части программной системы
- сбор и накопление статистики для будущих эвристических алгоритмов
фильтрации запросов
- счетчики загрузки и производительности информационной системы
В настоящее время для фильтрации запросов применяется шифрование,
"временной параметр" и отклонение сессией текущего запроса, если не
выполнен предыдущий. Возможно ошибочное завершение выполнение запроса по
time-out.
Графический интерфейс пользователя для работы с данными, как в
Интернете, так и локальной сети абсолютно идентичен и реализован в одном
приложении. Выбор канала обмена (Tcp или Http) задается в файле
"НастройкаWebRemoteServiceКлиента.txt" настройки приложения. Разница
в работе ощущается лишь по длительности операции загрузки страницы
данных с информационного SQL сервера в локальную базу данных.
Внешний вид панелей графического интерфейса пользователя
Клиентское приложение построено в форме много проектного Visual
Studio (VS-) решения (много файловой сборки), основу которого составляют
четыре проекта - clsApp, dbДоступ, clsStartCrypto и wcКаталогВидео.
Решение компилируется с использованием строгих имён.
Первый проект - clsApp, реализует построение класса общих
(глобальных) параметров решения, включая описание и определение
делегатов общих функций всего проекта. Он не ссылается ни на один другой
проект решения, но на него могут ссылаться все. Иными словами, его видят
все, он не видит никого. В последовательности компиляции проект clsApp
стоит первым номером. Особенностью данного класса является определение
здесь общих параметров как статических переменных:
static public dgtРегистрацияПользователя РегистрацияПользователя=null;
static public dgtАнализPinCod АнализPinCod=null;
Второй проект - dbДоступ, содержит реализацию локальной базы данных.
В последовательности компиляции он стоит вторым номером.
Третий проект - clsStartCrypto, является реализацией интерфейса
клиентского приложения с КриптоСервером. Это узловой проект всего
решения, и его сборка передаётся клиенту в зашифрованном виде.
Четвёртый проект - wcКаталогВидео, содержит ссылки на все другие
проекты решения. Он видит всех, его не видит никто. В последовательности
компиляции он последний. Содержит описание единого окна решения и
реализацию общих функций пользовательского интерфейса.
Остальные проекты или содержат описания оконных панелей графического
интерфейса и реализуют соответствующую функциональность, или реализуют
функциональность вывода отчетов (реализован вывод отчетов в форматах
HTML и CrystalReport).
Всё хорошо, но удручает отсутствие возможности выгрузки из памяти не
нужной сборки.
Также, для построения VS-решений применяется внешняя Zlib.dll,
реализующая функциональность компрессора данных.
Общий подход к настройке .Net сервисов и приложений
Файлы "app.config" и "Web.config" содержат дополнительный
фрагмент
<appSettings>
…..
<add key="pathSetting" value="XXX"/>
</appSettings>
XXX - или пусто "", или полный путь к папке с дополнительным
файлом настройки, например C:\НастройкаWebRemoteServiceКлиента\
Доступ к фрагменту:
string ss=System.Configuration.ConfigurationSettings.AppSettings["pathSetting"];
Доступ к дополнительному файлу настройки:
ss+="НастройкаWebRemoteServiceКлиента.txt";
StreamReader sr= new StreamReader(ss,System.Text.Encoding.Default,true);
Анализ файла настройки
//-- Анализ протокола - Tcp или Http
HostUrl = rp(sr);
if(HostUrl.IndexOf("tcp",0,3)<0)
{//-- Запуск RemoteService на IIS
HostUrl+=@"RemoteServiceOnWebServer/RemoteServiceOnIIS_Uri.soap";
fServer=false;
}
else
{//-- Запуск RemoteService на RemoteServer
HostUrl+=@"RemoteServer/rServer_KatalogVideo_Uri";
fServer=true;
}
и т.д. ...
Содержимое файла настройки "НастройкаWebRemoteServiceКлиента.txt":
//--Настройка WebService клиента и Remote Service клиента
//--==================================================
//-- фрагмент Url для Remote сервера или Web сервера
http://localhost:80/ //-- Фрагменты tcp://localhost:5000/ (Remote) http://localhost:80/(Web)
//--
//--Path доступ к каталогу файла HTML
C:\ НастройкаWebRemoteServiceКлиента
//--Path доступ к каталогу файла Crystal Report
C:\ НастройкаWebRemoteServiceКлиента
//--Path доступ к каталогу файла hshLP.bin
//-- этот каталог должен располагаться на личном носителе!!!
//-- (например - флоппи или в Cripto-папке личного profile),
!!! //--если "!!!", то hshLP.bin располагается в ["APPDATA"]+"\MyCrypto\" пользователя
Анализ дополнительного файла настройки
//-- Чтение параметра из файла настройки
private string rp(StreamReader sr)
{
string x,y,z;
int n,i;
//-- Пропуск строк комментария
do {
x=sr.ReadLine(); //-- Читаем строку файла настройки
if(x.Length<4) x+=" ";
}
while(x.Substring(0,4)==@"//--");
//-- Анализ строки
n=x.IndexOf(@"//--",0,x.Length); //-- Отбросим комментарии в конце строки,
//-- если они есть
n=(n<0)?x.Length:n;
y=x.Substring(0,n);
x="";
z="";
//-- отбрасываем символы управления, такие как "TAB"
for(i=0;i<n;i++) {
if(char.IsControl(y,i)){z=" ";}
else {
x=x+z+y.Substring(i,1);
z="";
}
}
return x.Trim();
}
Аннотация
Построен прототип защищенных информационных систем для работы с
конфиденциальной информацией SQL сервера, использующий открытые каналы
связи типа Интернет. Проработаны вопросы сериализации типов, компрессии
и шифрования данных, аутентификации и авторизации пользователей,
ограничения доступа, защиты клиентского приложения от
несанкционированной модификации, управления производительностью,
программной реализации проектов прототипа.