禁止されているリソースのレジストリと連携します

C#OpenSSLを使用して禁止リソースのレジストリを取得し、 MikroTik機器に基づくRouterOSを使用してフィルタリングする自動化



画像



注意! 2014年3月19日の最近の変更に応じて変更された記事





入門





仕事では、通信事業者のリマインダーによって導かれます。

作業は次のポイントで構成されています。

  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>
      
      











主なことは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.1gでした



はい、このアセンブリにはVisual C ++ 2008 Redistributablesがインストールされている必要があり、そこからダウンロードできます。

インストール。 「追加タスクの選択」ダイアログインストールする場合 「The OpenSSL」バイナリ(/ bin)ディレクトリを選択する必要があります。これ以上のトリックはありません。

次に、インストールしたフォルダー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;



次に、EDSが必要です。 信頼できる証明機関で購入できます。 キーは、 P12FromGostCSPユーティリティを使用して、Windowsの暗号コンテナーからPKCS#12形式でエクスポートする必要があります。



なぜP12FromGostCSP
LinuxでのPhp-soapインタラクションとGOSTアルゴリズムを使用した証明書認証からのxtronのコメントを引用



一見するとすべてが単純であるとは限りません。 実際、標準の証明書表示ダイアログからエクスポートされた証明書は、opensslで認識されません。 次のエラーが判明します。

MAC確認済みOK

バッグの属性

localKeyID:01 00 00 00

friendlyName:レジストリ\\ mstaff

Microsoft CSP名:Crypto-Pro GOST R 10/34/2001 Cryptographic Service Provider

キーと証明書の出力エラー

140017040754368:エラー:06074079:デジタルエンベロープルーチン:EVP_PBE_CipherInit:不明なpbeアルゴリズム:evp_pbe.c:167:TYPE = 1.2.840.113549.1.12.1.80

140017040754368:エラー:23077073:PKCS12ルーチン:PKCS12_pbe_crypt:pkcs12 algor cipherinitエラー:p12_decr.c:83:

140017040754368:エラー:2306A075:PKCS12ルーチン:PKCS12_item_decrypt_d2i:pkcs12 pbe暗号化エラー:p12_decr.c:130:



これがこのユーティリティであり、このようなエラーを回避できます。 このユーティリティの開発者を引用します。



P12FromGostCSPユーティリティによって作成されたPKCS#12コンテナーは、CryptoCom LLC(opensslプロジェクトの一部)およびTop Cross LLCによって作成された類似のコンテナーと完全に互換性があります。残念ながら、CryptoProに含まれるソフトウェアツールによって作成されたコンテナーについては言えませんCSP(バージョンR3以降)。



次のタイプのopensslまたはlirsslユーティリティを使用して、CryptoPro CSP R3ツールによって作成されたPKCS#12コンテナーおよび他の手段によって作成されたコンテナーのASN1構造を表示すると便利です。



#openssl asn1parse –inform DER –in <PKCS container#12>



これらの構造を比較すると、たとえば、ハッシュアルゴリズムGOST R 34.11-94の代わりに、CryptoProのコンテナーでSHA1が使用されていることがすぐにわかります。 次のコマンドを実行してコンテナの内容を表示しようとすると、さらに興味深い結果が得られます。



#openssl pkcs12 –in <PKCSコンテナー#12>







PKCS#12ファイルには、証明書と秘密キーが含まれている必要があります! 次のコマンドでこれを確認できます。



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







画像



次に、それをPEMに変換します。 OpenSSLでは、これは次のように行われます(コマンドラインから-PKCS#12キーを保護するパスワードを要求できます)。



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







画像



これで、誰もがリクエストに署名できるようになりました。



署名をリクエスト





次のコマンドを使用して、OpenSSLを介してリクエストに署名できます。



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







画像



C#でのファイル署名機能の実装:



 public static Boolean SignRequest() { Boolean ret = true; String OpenSSLPath = @"C:\OpenSSL\bin"; 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(); // dimzon541 if (!cmdProcess.WaitForExit(5000)) { cmdProcess.Kill(); ret = false; } } catch (Exception) { ret = false; } return ret; }
      
      







では、作業を確認してください。 電子 署名の 認証gosuslugi.ru 、選択- 電子文書の真正性の確認。 ES-切断済み、PKCS#7形式



OpenSSLを使用して確認できますが、そのためには、発行者の証明書を抽出する必要があります。これにより、EDSが提供され、PEM形式に変換されます(証明書はここで取得できます )。

 openssl x509 -inform der -in C:/most.cer -out C:/most.pem
      
      







そして署名を確認してください:

 openssl.exe smime -verify -in C:/request.xml.sign -content C:/request.xml -CAfile C:/most.pem -inform DER -out C:/validrequest.xml
      
      







その結果、次の行が表示されます。

 Verification successful
      
      





また、 request.xmlのコンテンツはvalidrequest.xmlファイルに含まれます。



禁止されているリソースのレジストリのダンプのリクエストに直接進みます。



リクエストを送信してレジストリをアップロードする





すべてが手動で簡単です: 要求送信フォーム 、要求ファイルとその署名( C:/request.xmlおよびC:/request.xml.sign )を送信します



画像



すべてが正常な場合、結果は-要求IDになります。これを使用して、要求の処理結果を確認できます。 原則として、すべてが正常であれば、アーカイブに5分後にZIP形式でレジストリがアンロードされます。2つのdump.xmlファイル (レジストリダンプとそのデジタル署名)があります。



OpenSSLまたはgosuslugi.ruでファイルを確認できます。

 openssl.exe smime -verify -in C:/dump.xml.sig -content C:/dump.xml -CAfile C:/cartk.pem -inform DER -out C:/validdump.xml
      
      







その結果、次の行が表示されます。

 Verification successful
      
      





そして、 dump.xmlの内容はvaliddump.xmlファイルにあります



次に、このプロセスを自動化します。 アンロードには、 SOAPアクセスプロトコル 、アドレス: serviceで動作するサービスがありますWSDLスキームは、 WSDLスキームで入手できます。



サービスは4つのメソッドで構成されています。







resultCommentタグに指定できる値:





問題を解決するためのフィードバック情報は、 このセクションのキャリアへのメモに記載されています。



サービスを操作するロジック:

  1. レジストリからのアンロードが更新されたかどうかを確認してください。 これを行うには、 getLastDumpDateExメソッドを呼び出して、取得した値を前の反復で取得した値と比較します。 lastDumpDateUrgentlyの値が変更された場合は、すぐに更新されたアンロードを要求してください。 それ以外の場合、裁量でアンロードを更新しますが、少なくとも1日に1回は更新します。
  2. アンロードが更新された場合、 sendRequestメソッドを使用してアンロードのリクエストを送信します。
  3. 数分後、 getResultメソッドを呼び出して、リクエストの処理結果を取得します。 要求がまだ処理されていない場合( resultCommentフィールドの内容を参照)、数分後に手順3を繰り返します。




サービスをリソースに追加し、WSDLスキームのアドレスを渡します。

機能コードgetLastDumpDatesendRequestgetResult



MaxReceivedMessageSize
最近、フォームのエラーが表示されるようになりました。着信メッセージの最大メッセージサイズのクォータを超えました( 65536 )。 クォータを増やすには、対応するバインディング要素のMaxReceivedMessageSizeプロパティを使用します。



したがって、 MaxReceivedMessageSizeプロパティを増やす必要がありました。

サポートコメント:

現時点では、getResultメソッドの応答には、サイズが65536バイトよりわずかに大きいxmlデータブロックが含まれています。






 public static Int64 LastDumpDate() { Int64 lastDumpDate = 0; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.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; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.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; BasicHttpBinding HttpBinding = new BasicHttpBinding(); HttpBinding.MaxReceivedMessageSize = 1*1024*1024*1024; //1Gb using (ChannelFactory<ServiceReference.OperatorRequestPortType> scf = new ChannelFactory<ServiceReference.OperatorRequestPortType>( HttpBinding, new EndpointAddress("http://vigruzki.rkn.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"))) { //...   }
      
      







レジストリからアンロードするための最後の更新のタイムスタンプは、次のようにUNIXタイムスタンプからDateTimeに変換できます。

 DateTime LastDumpDate = (new DateTime(1970, 1, 1, 0, 0, 0, 0)).AddSeconds(LastDumpDate()/1000);
      
      







レジストリダンプを解析する





アーカイブを解凍する必要があります。

 // Byte[] registerZipArchive -     GetResult(); File.WriteAllBytes(@"C:/register.zip", registerZipArchive); ZipFile.ExtractToDirectory(@"C:/register.zip", @"C:/register");
      
      







その結果、レジストリダンプCを取得します:/register/dump.xml

ダンプの内容の例:

 <?xml version="1.0" encoding="windows-1251"?> <reg:register updateTime="2014-02-02T12:00:00+04:00" xmlns:reg="http://rsoc.ru" xmlns:tns="http://rsoc.ru" updateTimeUrgently="2014-02-01T11:00:00"> <content id="68" includeTime="2013-12-01T10:00:05"> <decision date="2013-12-01" number="9" org=""/> <url><![CDATA[http://site1.com/index.php]]></url> <domain><![CDATA[site1.com]]></domain> <ip>1.1.1.1</ip> </content> <content id="68" includeTime="2013-12-01T10:00:05"> <decision date="2013-12-01" number="9" org=""/> <url><![CDATA[http://site2.com/page1.php]]></url> <url><![CDATA[http://site2.com/page2.php]]></url> <url><![CDATA[http://site2.com/page3.php]]></url> <domain><![CDATA[site2.com]]></domain> <ip>1.1.1.1</ip> <ip>1.1.1.2</ip> </content> <content id="9999" includeTime="2014-02-01T15:17:51" urgencyType="1"> <decision date="2014-02-01" number=" " org=""/> <url><![CDATA[http://site3.com/page1.html]]></url> <domain><![CDATA[site3.com]]></domain> <ip>1.2.3.4</ip> </content> </reg:register>
      
      







Parsim XML


これを行うには、2つのクラスを作成します。最初のRegisterDumpにはUpdateTimeフィールドとコンテンツオブジェクトのリストが含まれ、2番目のItemRegisterDumpはすべてのフィールドを持つ1つのコンテンツオブジェクトを表します。



 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://habrahabr.ru/post/187574/]]></url> * <ip>123.45.67.89</ip> * </content> * <content id="69" includeTime="2013-05-12T12:43:34"> * <decision date="2013-10-02" number="" org=""/> * <domain><![CDATA[chelaxe.ru]]></domain> * <ip>123.45.67.89</ip> * <ip>87.65.43.210</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 List<String> url { get; set; } public List<String> domain { get; set; } public List<String> ip { get; set; } public ItemRegisterDump() { id = String.Empty; includeTime = String.Empty; date = String.Empty; number = String.Empty; org = String.Empty; url = new List<String>(); domain = new List<String>(); ip = new List<String>(); } }
      
      







パーシム:



 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++) { ItemRegisterDump item = new ItemRegisterDump(); item.id = content[i].Attributes.GetNamedItem("id").InnerText; item.includeTime = content[i].Attributes.GetNamedItem("includeTime").InnerText; foreach (XmlNode node in content[i].ChildNodes) { switch(node.Name) { case "decision": item.date = node.Attributes.GetNamedItem("date").InnerText; item.number = node.Attributes.GetNamedItem("number").InnerText; item.org = node.Attributes.GetNamedItem("org").InnerText; break; case "url": item.url.Add(node.InnerText); break; case "domain": item.domain.Add(node.InnerText); break; case "ip": item.ip.Add(node.InnerText); break; } } 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







画像

画像

画像

画像



これで、サブストリングchelaxe.ruの内容を持つTCPを介してポート80に到着する192.168.0.0/24サブネットのすべてのパケット破棄されます。

コメントでは、碑文登録は簡単に追加されません。 更新されたルールを追加する前に、すべてのルールを削除する必要があります。



すべてのエントリを削除するスクリプトを次に示します。



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





/ip firewall filter remove [find comment=register]







画像



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.Read(); mikrotik.Send("/system/script/run"); mikrotik.Send("=number=cleaner", true); mikrotik.Read(); /* Cleaner * /ip firewall layer7-protocol remove [find comment=register] * /ip firewall filter remove [find comment=register] */ foreach (ItemRegisterDump item in dump.Items) { for (Int32 i = 0; i < item.domain.Count; i++ ) { mikrotik.Send("/ip/firewall/layer7-protocol/add"); mikrotik.Send("=name=" + item.id + "_" + i); mikrotik.Send("=comment=register"); mikrotik.Send("=regexp=^.+(" + item.domain[i] + ").*$", true); mikrotik.Read(); 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 + "_" + i); mikrotik.Send("=protocol=tcp"); mikrotik.Send("=src-address=" + SRCAddress); mikrotik.Send("=comment=register", true); mikrotik.Read(); } } } } catch (Exception) { ret = false; } return ret; }
      
      







この例では、ドメインブロッキングを使用しましたが、これは完全に真実ではありません。 URLでブロックするには、 layer7-protocolに次の正規表現を使用できます



^.*(/summary).*(chelaxe.ru).*$







さらに、 MikroTikエラーの結果として、5番目のブランチからのRouterOSのバージョンを使用する場合、式は小文字でなければなりません。



ソースはこちらから入手できます



作成がガイドされたとき:

キャリアメモ

OpenSSL 1.0.1c + libGOST.so + CryptoPro + ruTokenのキー

禁止されているリソースレジストリのデータベースへのアクセス方法

Roskomnadzorは、「海賊版」映画のあるサイトのレジストリの導入を発表しました

正規表現を学習、作成、およびテストするためのRegExr直感的なツール



All Articles