Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Трудимся с реестром запрещенных источников

Anna | 18.06.2014 | нет комментариев
Автоматизация приобретения реестра запрещенных источников средствами C#OpenSSL и фильтрация средствами RouterOS на базе оборудования MikroTik

image

Вводное

В работе будем руководствоваться памяткой оператора связи.
Работа состоит из следующих пунктов:

  1. Создание запроса
  2. Установка и настройка OpenSSL
  3. Подпись запроса
  4. Подача запроса и приобретение итога обработки запроса
  5. Обработка итога
  6. Добавление в фильтр на MikroTik`е

Создание запроса

Для подачи запроса на приобретение выгрузки из реестра нужно прикрепить файл запроса в формате XML. Файл имеет дальнейший вид:

<?xml version="1.0" encoding="windows-1251"?>
<request>
 <requestTime>2012-01-01T01:01:01.000 04:00</requestTime>
 <operatorName><![CDATA[Наименование оператора]]></operatorName>
 <inn>1234567890</inn>
 <ogrn>1234567890123</ogrn>
 <email>email@email.ru</email>
</request>

  • requestTime – дата и время образования запроса с указанием временной зоны;
  • operatorName – полное название оператора связи;
  • inn – ИНН оператора связи;
  • ogrn – ОГРН оператора связи;
  • email – электронный адрес технического эксперта, ответственного за применение механизма приобретения выгрузки; может
    применяться для оперативной обратной связи в случае происхождения технических вопросов либо задач.

Основное получить время в формате 2012-01-01T01:01:01.000 04:00 и сберечь файл с кодировкойwindows-1251.

Код функции генерации запроса на C#:

public static String GeneratingRequest(String operatorName, String inn, String ogrn, String email)
{
	String result = "<?xml version="1.0" encoding="windows-1251"?>";
	result  = "<request><requestTime>";
	result  = DateTime.Now.ToString("yyyy-MM-ddTHH:mm:ss.fffzzz");
	result  = "</requestTime><operatorName>";
	result  = "<![CDATA["   operatorName   "]]>";
	result  = "</operatorName><inn>";
	result  = inn;
	result  = "</inn><ogrn>";
	result  = ogrn;
	result  = "</ogrn><email>";
	result  = email;
	result  = "</email></request>";

	return result;
}

Сейчас сберегаем файл в кодировке windows-1251:

String Request = GeneratingRequest("Название оператора", "1234567890", "1234567890123", "email@email.ru")
StreamWriter swRequest = new StreamWriter(@"C:request.xml", false, Encoding.GetEncoding("Windows-1251"));
swRequest.Write(Request);
swRequest.Close();

Позже создания запроса нам нужно его подписать. Для этого воспользуемся криптографическим пакетом с открытым начальным кодом для работы с SSL/TLS — OpenSSL. Установим и настроем.

OpenSSL


Качаем пакет с этого сайта — slproweb.com. В моем случае это был Win64 OpenSSL v1.0.1e.

Да эта сборка требует для работы установленного Visual C 2008 Redistributables, тот, что дозволено скачать там же.
Устанавливаем. При установке в диалоге «Select Additional Tasks» следует предпочесть «The OpenSSL” binaries (/bin) directory» больше хитростей нет.
Дальше переходим в папку куда установили: C:/OpenSSL/bin и редактируем файл openssl.cfg. В предисловие файла добавим:

openssl_conf = openssl_def

В конце:
[openssl_def]
engines=engine_section

[engine_section]
gost=gost_section

[gost_section]
engine_id=gost
dynamic_path = C:/OpenSSL/bin/gost.dll
default_algorithms=ALL

Все фактически теснее работает осталось настроить переменные среды:
OPENSSL_CONF = C:/OpenSSL/bin/openssl.cfg — полный путь к openssl.cfg
ну и в PATH = C:/OpenSSL/bin;

Сейчас нам необходима ЭЦП. Купить ее дозволено в доверенном удостоверяющем центре. Ключ нужно экспортировать в формате PKCS#12 из криптоконтейнера в Windows с поддержкой утилиты P12FromGostCSP

Дальше преобразовать его в PEM. В OpenSSL это делается так (через командную строку — может запросить пароль которым защищен Ключ PKCS#12):

openssl.exe pkcs12 -in C:/key.pfx -out C:/key.pem -nodes -clcerts

image

Все сейчас мы в состоянии подписать наш запрос.

Подпись запроса

Подписать запрос дозволено через OpenSSL дальнейшей командой:

openssl.exe smime -sign -in C:/request.xml -out C:/request.xml.sign -signer C:/key.pem -outform DER

image

Реализация функции подписи файла на C#:

public static Boolean SignRequest()
{
	Boolean ret = true;

	String OpenSSLPath = @"C:OpenSSLbin";
	String RequestPath = @"C:request.xml";   
	String SignRequestPath = @"C:request.xml.sign";
	String KeyPEMPath = @"C:key.pem";

	try
	{

		Process cmdProcess = new Process();

		/*
		 * Строку ниже дозволено убрать 
		 * если переменная среды PATH
		 * имеет путь до OpenSSL
		 */
		cmdProcess.StartInfo.WorkingDirectory = OpenSSLPath;
		cmdProcess.StartInfo.FileName = "openssl.exe";
		cmdProcess.StartInfo.Arguments = String.Format("smime -sign -in {0} -out {1} -signer {2} -outform DER", RequestPath, SignRequestPath, KeyPEMPath);

		cmdProcess.StartInfo.CreateNoWindow = true;
		cmdProcess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
		cmdProcess.Start();
		//Задержка нужна т.к. необходимо дождаться конца операции
		Thread.Sleep(2500);
	}
	catch (Exception)
	{
		ret = false;
	}

	return ret;
}

Сейчас проверим нашу работу. Доказательство подлинности ЭП: gosuslugi.ru, выбираем — Доказательство подлинности электронного документа. ЭП — отсоединенная, в формате PKCS#7

Переходим непринужденно к запросу дампа реестра запрещенных источников.

Подача запроса и выгрузка реестра

В ручную все легко: Форма подачи запроса, отправляем файл запроса и его подпись (C:/request.xml иC:/request.xml.sign)

image

Если все типично то итогом будет — Идентификатор запроса, с поддержкой его дозволено проверять итогобработки запроса. Как правило если все типично то реестр будет выгружен минут через 5 в ZIP формате в архиве будет два файла dump.xml — дамп реестра и его цифровая подпись.

Сейчас автоматизируем данный процесс. Для выгрузки есть сервис работающий по протоколу доступа SOAP, адрес: обслуживанияWSDL схема доступна по адресу: WSDL схема.

Сервис состоит из 3-х способов:

  • getLastDumpDate
    Способ предуготовлен для приобретения временной метки последнего обновления выгрузки из
    реестра, long формат, так и не осознал как разобрать эту дату, берегу как есть, если кто в курсе, то прокомментируйте.
  • sendRequest
    Способ предуготовлен для направления запроса на приобретение выгрузки из реестра, ПринимаетrequestFile и signatureFile в base64Binary формате — файл запроса и его подписи (C:/request.xml иC:/request.xml.sign) В результат присылает result — Итог обработки запроса в boolean формате и если все благополучно, то code — Идентификатор запроса, строка по которой нужно получить выгрузку из реестра в формате string. Так же resultComment — Комментарий к итогу обработки запроса в формате string.
  • getResult
    Способ предуготовлен для приобретения итога обработки запроса — выгрузки из реестра, принимаетcode — Идентификатор запроса, строка по которой нужно получить выгрузку из реестра в форматеstring. В результат присылает result — Итог обработки запроса в boolean формате и если все успешно, то registerZipArchive — Файл zip-архив с выгрузкой из реестра в формате base64Binary. Так жеresultComment — Комментарий к итогу обработки запроса в формате string.

Логика примитивна: Получаем временную метку последнего обновления реестра, если он изменилась, то направляем запрос на приобретение выгрузки из реестра, если все успешно, то через 5 минут забираем наш реестр либо ожидаем еще чуть чуть…

Добавляем в источники сервис, передавая адрес WSDL схемы.
Код функций getLastDumpDatesendRequestgetResult

public static Int64 LastDumpDate()
{
	Int64 lastDumpDate = 0;

	using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>(
		new BasicHttpBinding(), new EndpointAddress("http://zapret-info.gov.ru/services/OperatorRequest/")))
	{
		ServiceReference.OperatorRequestPortType channel = scf.CreateChannel();
		ServiceReference.getLastDumpDateResponse glddr = channel.getLastDumpDate(new ServiceReference.getLastDumpDateRequest());
		lastDumpDate = glddr.lastDumpDate;
	}

	return lastDumpDate;
}

public static Boolean SendRequest(out String resultComment, out String code, Byte[] requestFile, Byte[] signatureFile)
{
	Boolean result = false;
	code = null;

	using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>(
		new BasicHttpBinding(), new EndpointAddress("http://zapret-info.gov.ru/services/OperatorRequest/")))
	{
		ServiceReference.OperatorRequestPortType channel = scf.CreateChannel();
		ServiceReference.sendRequestRequestBody srrb = new ServiceReference.sendRequestRequestBody();

		srrb.requestFile = requestFile;
		srrb.signatureFile = signatureFile;

		ServiceReference.sendRequestResponse srr = channel.sendRequest(new ServiceReference.sendRequestRequest(srrb));

		resultComment = srr.Body.resultComment;

		if (result = srr.Body.result)
		{
			code = srr.Body.code;
		}
	}

	return result;
}

public static Boolean GetResult(out String resultComment, out Byte[] registerZipArchive, String code)
{
	Boolean result = false;
	registerZipArchive = null;

	using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>(
		new BasicHttpBinding(), new EndpointAddress("http://zapret-info.gov.ru/services/OperatorRequest/")))
	{
		ServiceReference.OperatorRequestPortType channel = scf.CreateChannel();
		ServiceReference.getResultRequestBody grrb = new ServiceReference.getResultRequestBody();

		grrb.code = code;

		ServiceReference.getResultResponse grr = channel.getResult(new ServiceReference.getResultRequest(grrb));

		resultComment = grr.Body.resultComment;

		if (result = grr.Body.result)
		{
			registerZipArchive = grr.Body.registerZipArchive;
		}
}

	return result;
}

Скажем отправим запрос:

String resultComment, code;
if(SendRequest(out resultComment, out code, File.ReadAllBytes(@"C:/request.xml"), File.ReadAllBytes(@"C:/request.xml.sign")))
{
	//... Все успешно
}

Разбираем дамп реестра

Нужно распаковать архив:

// Byte[] registerZipArchive - Получен при успешной выгрузке GetResult();
File.WriteAllBytes(@"C:/register.zip", registerZipArchive);
ZipFile.ExtractToDirectory(@"C:/register.zip", @"C:/register");

Парсим XML


Для этого сделаем два класса: 1-й RegisterDump будет содержать поле UpdateTime и список объектовcontent, 2-й ItemRegisterDump представляет один объект content со всеми полями.

public class RegisterDump
{
	/*
	 * <reg:register updateTime="2013-07-15T10:05:00 04:00" xmlns:reg="http://rsoc.ru" xmlns:tns="http://rsoc.ru">
	 *    <content></content>
	 *    <content></content>
	 *       ...
	 *    <content></content>
	 * </reg:register>
	 */

	public List<ItemRegisterDump> Items { get; set; }
	public String UpdateTime { get; set; }

	public RegisterDump()
	{
		this.Items = new List<ItemRegisterDump>();
		this.UpdateTime = String.Empty;
	}

	public RegisterDump(String UpdateTime, List<ItemRegisterDump> Items)
	{
		this.Items = Items;
		this.UpdateTime = UpdateTime;
	}
}

public class ItemRegisterDump
{
	/*
	 * <content id="60" includeTime="2013-01-12T16:33:38">
	 *    <decision date="2013-11-03" number="МИ-6" org="РосКосМопсПопс"/>
	 *    <url><![CDATA[http://chelaxe.ru/blacklist/]]></url>
	 *    <domain><![CDATA[chelaxe.ru]]></domain>
	 *    <ip>123.45.67.89</ip>
	 * </content>
	 */

	public String id { get; set; }
	public String includeTime { get; set; }

	public String date { get; set; }
	public String number { get; set; }
	public String org { get; set; }

	public String url { get; set; }
	public String domain { get; set; }
	public String ip { get; set; }

	public ItemRegisterDump()
	{
		id = String.Empty;
		includeTime = String.Empty;

		date = String.Empty;
		number = String.Empty;
		org = String.Empty;

		url = String.Empty;
		domain = String.Empty;
		ip = String.Empty;
	}
}

Парсим:

RegisterDump Register = new RegisterDump();
String dumpfile = @"C:/register/dump.xml";

XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(dumpfile);

Register.UpdateTime = xmlDoc.GetElementsByTagName("reg:register")[0].Attributes.GetNamedItem("updateTime").InnerText;
XmlNodeList content = xmlDoc.GetElementsByTagName("content");

for (int i = 0; i < content.Count; i  )
{
	XmlNodeList nodechild = content[i].ChildNodes;

	ItemRegisterDump item = new ItemRegisterDump();

	item.id = content[i].Attributes.GetNamedItem("id").InnerText;
	item.includeTime = content[i].Attributes.GetNamedItem("includeTime").InnerText;

	item.date = nodechild[0].Attributes.GetNamedItem("date").InnerText;
	item.number = nodechild[0].Attributes.GetNamedItem("number").InnerText;
	item.org = nodechild[0].Attributes.GetNamedItem("org").InnerText;

	item.url = nodechild[1].InnerText;
	item.domain = nodechild[2].InnerText;
	item.ip = nodechild[3].InnerText;

	Register.Items.Add(item);
}

Сейчас блокируем все это благо на нашем MikroTik.

Блокируем средством MikroTik


Делаем мы это с поддержкой layer7-protocol и добавляем в фильтры.

Вот пример:
/ip firewall layer7-protocol add name=12 comment=register regexp=^. (chelaxe.ru).*$
/ip firewall filter add action=drop chain=forward disabled=no dst-port=80 layer7-protocol=12 protocol=tcp src-address=192.168.0.0/24 comment=register

image
image
image
image

Все сейчас все пакеты для подсети 192.168.0.0/24 приходящие по TCP на 80 порт с оглавлением подстрокиchelaxe.ru отбрасываются.
В комментарии надпись register добавляется не с примитивна. Она необходима нам будет для удаления всех правил раньше чем добавить обновленные.

Вот скрипт тот, что удалит все записи:

/ip firewall layer7-protocol remove [find comment=register]
/ip firewall filter remove [find comment=register]

image

Общение с MikroTik роутером будет посредством API, нужную библиотеку с примерами нам теснее написали:wiki/API C#

Применяя эту библиотеку (класс) и правило блокирования контента разобранный выше реализуем все это на C#

public static Boolean AddFilterL7(String ip, String username, String password, RegisterDump dump, String SRCAddress)
{
	Boolean ret = true;

	try
	{
		//Класс MK смотри тут http://wiki.mikrotik.com/wiki/API_in_C_Sharp
		MK mikrotik = new MK(IPAddress.Parse(ip).ToString());

		if (mikrotik.Login(username, password))
		{			
			mikrotik.Send("/system/script/add");
			mikrotik.Send("=name=cleaner");
			mikrotik.Send("=source=/ip firewall layer7-protocol remove [find comment=register]n/ip firewall filter remove [find comment=register]", true);

			mikrotik.Send("/system/script/run");
			mikrotik.Send("=number=cleaner", true);

			/* Cleaner
			 * /ip firewall layer7-protocol remove [find comment=register]
			 * /ip firewall filter remove [find comment=register]
			 */

			//Ждем пока все ветхие записи удалятся
			Thread.Sleep(1000);

			foreach (ItemRegisterDump item in dump.Items)
			{
				//Ждем немножко Дабы MikroTik не захлебнулся
				Thread.Sleep(100);

				// /ip firewall layer7-protocol add name=12 comment=register regexp=^. (chelaxe.ru).*$
				mikrotik.Send("/ip/firewall/layer7-protocol/add");
				mikrotik.Send("=name="   item.id);
				mikrotik.Send("=comment=register");
				mikrotik.Send("=regexp=^. ("   item.domain   ").*$", true);

				//Ждем немножко Дабы MikroTik не захлебнулся
				Thread.Sleep(100);

				// /ip firewall filter add action=drop chain=forward disabled=no dst-port=80 layer7-protocol=12 protocol=tcp src-address=192.168.0.0/24 comment=register
				mikrotik.Send("/ip/firewall/filter/add");
				mikrotik.Send("=action=drop");
				mikrotik.Send("=chain=forward");
				mikrotik.Send("=disabled=no");
				mikrotik.Send("=dst-port=80");
				mikrotik.Send("=layer7-protocol="   item.id);
				mikrotik.Send("=protocol=tcp");
				mikrotik.Send("=src-address="   SRCAddress);
				mikrotik.Send("=comment=register", true);                        
			}
		}

		Thread.Sleep(200);
		mikrotik.Close();
	}
	catch (Exception)
	{
		ret = false;
	}

	return ret;
}

Ну вот и все.

Исходники доступны тут.

 

Источник: programmingmaster.ru

 

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB