はじめに
WCF で HTTPS を構成できるか試すことになった。 しかも IIS を使わず、セルフホストで。
サービスコントラクトの定義
using System.ServiceModel; namespace WcfHttpsSample.Shared { [ServiceContract] public interface ISampleService { [OperationContract] string Echo(string message); } }
WCF サービスの実装
using System; using System.Net; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using WcfHttpsSample.Shared; namespace WcfHttpsSample.Server { class Program { static void Main(string[] args) { var baseAddress = new Uri($"https://{Dns.GetHostEntry("").HostName}:8080"); var host = new ServiceHost( typeof(SampleService), baseAddress); // https で待ち受けられるようにする var binding = new BasicHttpBinding(); binding.Security.Mode = BasicHttpSecurityMode.Transport; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; host.AddServiceEndpoint( typeof(ISampleService), binding, "Sample"); host.Open(); Console.WriteLine("Enter で終了"); Console.ReadLine(); host.Close(); } } public class SampleService : ISampleService { public string Echo(string message) => message; } }
WCF クライアントの実装
using System; using System.Net; using System.Net.Security; using System.ServiceModel; using System.ServiceModel.Channels; using WcfHttpsSample.Shared; namespace WcfHttpsSample.Client { class Program { static void Main(string[] args) { // 自己署名証明書を使って試す場合は証明書の検証をしない ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback((a, b, c, d) => true); var endpointAddress = new EndpointAddress( $"https://{Dns.GetHostEntry("").HostName}:8080/Sample"); // https で通信できるようにする var binding = new BasicHttpBinding(); binding.Security.Mode = BasicHttpSecurityMode.Transport; binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None; var channel = ChannelFactory<ISampleService>.CreateChannel( binding, endpointAddress); var reply = channel.Echo("Hello World!"); Console.WriteLine(reply); ((IChannel)channel).Close(); Console.WriteLine("Enter で終了"); Console.ReadLine(); } } }
SSL 証明書をインポート
HTTP.sys から見られるよう、ローカルマシンの個人にインポートする。 ローカルマシンにインストールしておかないと、SSL 証明書をポート番号にバインドできない。 そのせいで数時間ハマった。
SSL 証明書をポート番号にバインド
このままだと、クライアントからアクセスした際に下記のような例外が発生する。
System.ServiceModel.CommunicationException: 'https://ホスト名:8080/Sample に対する HTTP 要求の発行中にエラーが発生しました。 この原因としては、HTTPS ケースの HTTP.SYS でサーバー証明書が正しく構成されていないこと、 またはクライアントとサーバーの間でセキュリティ バインドが整合していないことが考えられます。
下記のコマンドを実行して、SSL 証明書をポート番号にバインドする必要がある。
netsh http add sslcert ipport=0.0.0.0:8080 certhash=<証明書のサムプリント> appid={<適当な Guid>}
appid は適当な GUID で良いみたい。
実行結果
おわりに
早く gRPC に移行したい。