実現したいこと(目的)
HTTPプロトコルを介して他のコンピューター上のアプリケーションとデータを交換したい。
要件:
- 盗聴、改竄、なりすまし対策が施されている。
- セルフホストである(IISは使用しない。)
試したこと
- WCFを使用
- RESTFul方式(WebHttpBinding)を使用
- SSLを使用
但し、サーバー証明書をコンピューターにインポートする手順に未精通なため、サービスのアプリケーションが直接pfxファイルを読み込んで行う方式で実装している。
問題点
エラー(後述)が発生して、データを交換できない。
よろしくお願い致します。
どうぞよろしくお願い致します。
該当のソースコード
サービス
Program.cs
C#
1using System; 2using System.Security.Cryptography.X509Certificates; 3using System.ServiceModel; 4using System.ServiceModel.Description; 5using System.ServiceModel.Security; 6using System.ServiceModel.Web; 7 8namespace TestService 9{ 10 class Program 11 { 12 static WebServiceHost host; 13 14 static void Main() 15 { 16 //--------------------------バインディング 17 WebHttpBinding binding = new WebHttpBinding(); 18 binding.Security.Mode = WebHttpSecurityMode.Transport; 19 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; 20 binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.None; 21 22 //--------------------------アドレス 23 Uri uri = new Uri("https://localhost:5000/TestService"); 24 25 //--------------------------サービスの作成とエンドポイントの追加 26 host = new WebServiceHost(typeof(TestService)); 27 ServiceEndpoint se = host.AddServiceEndpoint(typeof(ITestService), binding, uri); 28 29 var behavior = new WebHttpBehavior(); 30 behavior.FaultExceptionEnabled = false; 31 behavior.HelpEnabled = true; 32 behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json; 33 behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 34 se.EndpointBehaviors.Add(behavior); 35 36 //-------------------------- 37 ServiceDebugBehavior debug = host.Description.Behaviors.Find<ServiceDebugBehavior>(); 38 debug.IncludeExceptionDetailInFaults = true; 39 40 ServiceMetadataBehavior metad = new ServiceMetadataBehavior(); 41 metad.HttpGetEnabled = true; 42 metad.HttpsGetEnabled = true; 43 host.Description.Behaviors.Add(metad); 44 45 //-------------------------- 46 var certificate = new X509Certificate2(@"D:\Work\TestService\ServerCert1.pfx", "pswd", X509KeyStorageFlags.UserKeySet); 47 host.Credentials.ServiceCertificate.Certificate = certificate; 48 host.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; 49 50 //-------------------------- 51 host.Open(); 52 Console.WriteLine(string.Format(null, "URL : {0}", uri.ToString())); 53 Console.WriteLine("Press <ENTER> to terminate"); 54 Console.ReadLine(); 55 host.Close(); 56 57 } 58 } 59} 60 61
ITestService.cs
C#
1using System.Runtime.Serialization; 2using System.ServiceModel; 3using System.ServiceModel.Web; 4 5namespace TestService 6{ 7 [ServiceContract] 8 interface ITestService 9 { 10 [OperationContract] 11 [WebInvoke(Method = "POST" 12 , RequestFormat = WebMessageFormat.Json 13 , UriTemplate = "/PostMsg" 14 )] 15 MessageData PostMsg(MessageData msg); 16 } 17 18 [DataContract] 19 public class MessageData 20 { 21 [DataMember] 22 public string Name { get; set; } 23 24 [DataMember] 25 public int Gender { get; set; } 26 27 [DataMember] 28 public int Age { get; set; } 29 30 } 31 32} 33 34
TestService.cs
C#
1using System; 2 3namespace TestService 4{ 5 class TestService : ITestService 6 { 7 public MessageData PostMsg(MessageData msg) 8 { 9 Console.WriteLine(string.Format(null, "受信 : {0}", msg.ToString())); 10 11 return new MessageData() 12 { 13 Name = msg.Name, 14 Gender = msg.Gender, 15 Age = msg.Age + 1 16 }; 17 } 18 19 } 20} 21 22
サービス利用側
Form1.cs
C#
1using System; 2using System.Windows.Forms; 3using System.Security.Cryptography.X509Certificates; 4using System.ServiceModel; 5using System.ServiceModel.Description; 6using System.ServiceModel.Security; 7using System.ServiceModel.Web; 8using TestService; 9 10namespace TestClient 11{ 12 public partial class Form1 : Form 13 { 14 WebChannelFactory<ITestService> cf = null; 15 ITestService channel = null; 16 17 public Form1() 18 { 19 InitializeComponent(); 20 } 21 22 private void Form1_Load(object sender, EventArgs e) 23 { 24 listBox1.HorizontalScrollbar = true; 25 26 Uri uri = new Uri("https://123.123.123.123:5000/TestService"); 27 EndpointAddress endpointAddress = new EndpointAddress(uri); 28 29 //--------------------------チャンネル 30 cf = new WebChannelFactory<ITestService>(uri); 31 WebHttpBinding binding = cf.Endpoint.Binding as WebHttpBinding; 32 binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate; 33 binding.Security.Mode = WebHttpSecurityMode.Transport; 34 35 var behavior = new WebHttpBehavior(); 36 behavior.FaultExceptionEnabled = false; 37 behavior.HelpEnabled = true; 38 behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json; 39 behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json; 40 cf.Endpoint.Behaviors.Add(behavior); 41 42 var clientCertificate = new X509Certificate2(@"D:\Work\TestService\ServerCert1.pfx", "pswd", X509KeyStorageFlags.UserKeySet); 43 cf.Credentials.ClientCertificate.Certificate = clientCertificate; 44 cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None; 45 46 channel = cf.CreateChannel(); 47 48 } 49 50 private void button1_Click(object sender, EventArgs e) 51 { 52 MessageData msg = new MessageData() 53 { 54 Name = "Taro", 55 Gender = 1, 56 Age = 3 57 }; 58 MessageData rtn = channel.PostMsg(msg); //<--------ここでエラー発生 59 listBox1.Items.Insert(0, string.Format("Name:{0}, Gender:{1}, Age{2} ", rtn.Name, rtn.Gender, rtn.Age)); 60 61 } 62 } 63} 64 65
エラー内容
https://123.123.123.123:5000/TestService/PostMsg に対する HTTP 要求の発行中にエラーが発生しました。この原因としては、HTTPS ケースの HTTP.SYS でサーバー証明書が正しく構成されていないこと、またはクライアントとサーバーの間でセキュリティ バインドが整合していないことが考えられます。
証明書作成
DOS
1自己証明機関の作成 2makecert -n "CN=ServerCN1" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv ServerCert1.pvk ServerCert1.cer -ss Root -sr localMachine -cy authority -b 11/06/2019 -e 12/31/2019 3 4ソフトウェア発行元証明書の作成 5cert2spc ServerCert1.cer ServerCert1.spc 6 7個人情報交換ファイルの作成 8pvk2pfx -pvk ServerCert1.pvk -spc ServerCert1.spc -po pswd -pfx ServerCert1.pfx -f
#試したこと1
①パワーシェルで、以下のコマンドを実行し、証明書の捺印を取得
Get-PfxCertificate -FilePath ServerCert1.pfx
②トランスポート層で X.509 証明書を使用して認証するクライアントをサポートするために
netsh http add sslcert ipport=0.0.0.0:5000 certhash=9512A6EA3042F7717EA02194097187207136E5FB appid={541eea84-c788-4d23-b6b2-f5210bcdf5c5} clientcertnegotiation=enable
を実行 ※certhashに①で取得した捺印を、appidには、Visual StudioのサービスプロジェクトのプロパティウィンドウにあるGUIDを設定。
===>下記エラー発生
SSL 証明書を追加できませんでした。エラー: 1312
指定されたログオン セッションは存在しません。そのセッションは既に終了している可能性があります。
#試したこと2
サービスとクライアントの各ソースコードからセキュリティ関連のコードを除去した場合は、正常なメッセージのやり取りが実現できています。
#試したこと3
X509Certificate2メソッドのパスワードの引数をわざと違うものに設定するとちゃんとエラーになります。
#補足1
IISでは.pfxファイルをコンピュータにインポートするが、今回の試みでは、X509Certificate2メソッドの引数にファイルを指定している。
#補足2
HTTPS通信は、共通鍵暗号方式と公開鍵暗号方式のハイブリッドで成り立っているらしい。
環境
Microsoft Windows 10 Pro (Version 1903)
Microsoft Visual Studio Community 2019
Microsoft .NET Framework 4.7.2
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2019/11/10 18:05
2019/11/10 18:56
2019/11/11 10:14 編集
2019/11/11 13:15
2019/11/12 01:35
2019/11/12 05:07
2019/11/12 05:34
2019/11/12 05:48
2019/11/12 06:40
2019/11/12 08:19
2019/11/12 08:23 編集
2019/11/14 03:53
2019/11/14 06:16