GIS住宅および共同サービス:安全な接続を確立し、WCFを使用してメッセージに署名する

現在、住宅および共同サービスの州情報システムが積極的に最終化されており、2017年1月1日から、システムで情報を提供しない管理者および資源供給組織の責任が生じています。



実践が示すように、情報システムをGISハウジングおよび共同サービスと統合する開発者は、安全な接続の確立とメッセージへの署名に多くの時間を費やします。 GISハウジングおよび共同サービスの開発者がメッセージ署名するためのデモアプリケーションを提供したという事実にもかかわらず、WCFを使用してこの問題を解決する方法を説明します。 この場合、安全な接続を確立するために追加のソフトウェア(stunnelなど)は必要ありません。



この記事が、将来GISユーティリティと統合する開発者に役立つことを願っています。



残念ながら、追加のソフトウェアを購入せずにはできません。 CryptoPro .NETが必要です 。 3か月の無料使用期間があります。 このライブラリは、XAdES-BESアルゴリズムを使用してhttps接続とメッセージ署名を提供します。 また、資格のある電子署名の証明書が必要であることを忘れないでください。



準備する



最初に、ローカルコンピューターの個人用ストレージに修飾された電子署名証明書をインストールする必要があります。 秘密鍵とともにインストールする必要があります。

ここではプロセスの詳細を説明しません。情報はこちらこちらにあります



プロキシクラスの生成



GIS住宅および公共サービスのWebサイトの規制と指示セクションには、「外部情報システムとGIS住宅および公共サービスとの情報のやり取りに関する規制と形式」というファイルがあります。 現在のバージョンは10.0.1.2です。 このファイルにはwsdlファイルとxsdファイルが含まれています。これらを使用してWCFプロキシクラスを作成します。



すべてのwsdlおよびxsdファイルをフォルダー(たとえば、「c:/ gis」)にコピーします。 これは、生成ユーティリティがサービスxsdファイルが使用するすべての基本的なxsdファイルを見つけることができるようにするために必要です。



プロキシクラスを生成するには、標準ユーティリティSvcUtilを使用します。



"C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.6.1 Tools\SvcUtil.exe" c:/gis/hcs-nsi-common-service.wsdl c:/gis/*.xsd /messageContract /enableDataBinding /syncOnly /directory:"c:/gis/proxies" /noConfig /noLogo /out:NsiCommonService.cs /namespace:*,Gis.Infrastructure.NsiCommonService
      
      





このコマンドは、一般的なディレクトリを取得するためのプロキシクラスを作成します(hcs-nsi-common)。 このコマンドは、wsdlおよびxsdサービス記述ファイルの場所、結果のcsファイルの配置場所、および名前空間の名前付け方法を示します。 同様に、GISユーティリティの残りのサービスに対してこのコマンドを実行する必要があります。



生成されたプロキシクラスをプロジェクトに追加します



アプリケーション構成の構成



次のセクションをアプリケーション構成に追加します



 <configuration> <system.serviceModel> <extensions> <behaviorExtensions> <add name="MessageInspectorBehavior" type="Gis.Crypto.MessageInspectorBehaviorElement, Gis" /> </behaviorExtensions> </extensions> <behaviors> <endpointBehaviors> <behavior name="clientCertificateConf"> <clientCredentials> <!--   findValue     --> <clientCertificate findValue="" storeLocation="LocalMachine" x509FindType="FindBySerialNumber" /> <serviceCertificate> <authentication certificateValidationMode="None" revocationMode="NoCheck" /> </serviceCertificate> </clientCredentials> <MessageInspectorBehavior /> </behavior> </endpointBehaviors> </behaviors> <bindings> <customBinding> <binding> <textMessageEncoding messageVersion="Soap11"> <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="16348" maxBytesPerRead="4096" maxNameTableCharCount="16384" /> </textMessageEncoding> <httpsTransport authenticationScheme="Basic" useDefaultWebProxy="false" requireClientCertificate="true" maxBufferSize="2147483647" maxReceivedMessageSize="2147483647" /> </binding> </customBinding> </bindings> <client> <endpoint address="https://api.dom.gosuslugi.ru/ext-bus-nsi-common-service/services/NsiCommon" binding="customBinding" behaviorConfiguration="clientCertificateConf" contract="Gis.Infrastructure.NsiCommonService.NsiPortsType" name="NsiCommonPort" /> </client> </system.serviceModel> </configuration>
      
      





クラスの説明



設定で、MessageInspectorBehaviorを登録します。これにより、ClientMessageInspectorが追加されます。



 public class MessageInspectorBehavior : IEndpointBehavior { public void Validate(ServiceEndpoint endpoint) { } public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { SignatureMessageInspector inspector = new SignatureMessageInspector(); clientRuntime.MessageInspectors.Add(inspector); } }
      
      





また、メッセージの署名を行うSignatureMessageInspectorクラスのリストも提供します。



 public class SignatureMessageInspector : IClientMessageInspector { public object BeforeSendRequest(ref Message request, IClientChannel channel) { string st = GetSignElement(MessageString(ref request)); //place for log request request = CreateMessageFromString(st, request.Version); return null; } public void AfterReceiveReply(ref Message reply, object correlationState) { string st = MessageString(ref reply); //place for log response reply = CreateMessageFromString(st, reply.Version); } public static string GetSignElement(string messageString) { var originalDoc = new XmlDocument { PreserveWhitespace = true }; originalDoc.LoadXml(messageString); var nodes = originalDoc.SelectNodes($"//node()[@Id='{CryptoConsts.CONTAINER_ID}']"); if (nodes == null || nodes.Count == 0) { return originalDoc.OuterXml; } var gostXadesBesService = new GostXadesBesService(); string st = gostXadesBesService.Sign(messageString, CryptoConsts.CONTAINER_ID, CryptoConsts.CERTIFICATE_THUMBPRINT, string.Empty); return st; } Message CreateMessageFromString(String xml, MessageVersion ver) { return Message.CreateMessage(XmlReaderFromString(xml), int.MaxValue, ver); } XmlReader XmlReaderFromString(String xml) { var stream = new MemoryStream(); // NOTE: don't use using(var writer ...){...} // because the end of the StreamWriter's using closes the Stream itself. // var writer = new StreamWriter(stream); writer.Write(xml); writer.Flush(); stream.Position = 0; return XmlReader.Create(stream); } String MessageString(ref Message m) { // copy the message into a working buffer. MessageBuffer mb = m.CreateBufferedCopy(int.MaxValue); // re-create the original message, because "copy" changes its state. m = mb.CreateMessage(); Stream s = new MemoryStream(); XmlWriter xw = XmlWriter.Create(s); mb.CreateMessage().WriteMessage(xw); xw.Flush(); s.Position = 0; byte[] bXml = new byte[s.Length]; s.Read(bXml, 0, (int) s.Length); // sometimes bXML[] starts with a BOM if (bXml[0] != (byte) '<') { return Encoding.UTF8.GetString(bXml, 3, bXml.Length - 3); } return Encoding.UTF8.GetString(bXml, 0, bXml.Length); } }
      
      





使用する



GISハウジングおよび共同サービスへの直接リクエストを実行するには、生成されたプロキシクラスのクライアントインスタンスを作成し、クライアント認証設定を指定し、リクエストオブジェクトを形成し、必要なメソッドを呼び出す必要があります。



 class Program { static void Main(string[] args) { var service = new NsiPortsTypeClient(); service.ClientCredentials.UserName.UserName = "lanit"; service.ClientCredentials.UserName.Password = "tv,n8!Ya"; var request = new exportNsiListRequest1 { ISRequestHeader = new HeaderType { Date = DateTime.Now, MessageGUID = Guid.NewGuid().ToString() }, exportNsiListRequest = new exportNsiListRequest { version = "10.0.1.2", ListGroup = ListGroup.NSI, ListGroupSpecified = true, Id = CryptoConsts.CONTAINER_ID } }; var result = service.exportNsiList(request); } }
      
      





» プロジェクトのソースコード



All Articles