teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

3

一部、表現を修正

2019/11/11 17:57

投稿

dodox86
dodox86

スコア9416

answer CHANGED
@@ -10,7 +10,7 @@
10
10
  先の回答は外していました。くやしかった(?)のと、お手を煩わせてしまった罪滅ぼしに当方でも検証してみました。
11
11
  結果から書きますと、以下の処置で限定的ながら通信できるようになりました。
12
12
 
13
- 0. 作成した自己(オレオレ)証明書の*.pfxファイルをWindows証明書マネージャでインポート
13
+ 0. 作成した自己証明書の*.pfxファイルをWindows証明書マネージャでインポート
14
14
  0. インポートしたWindows証明書マネージャで証明書のサムプリント(拇印)を取得
15
15
  0. `netsh`コマンドでポート構成をバインド
16
16
  0. サーバー側プログラムで、匿名アクセスを受け付けるよう修正
@@ -18,8 +18,8 @@
18
18
 
19
19
  以下、順にご説明します。
20
20
 
21
- ### 1. 作成した自己(オレオレ)証明書の*.pfxファイルをWindows証明書マネージャでインポート
22
- 質問者さんの「試したこと1」で、netsh コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellのGet-PfxCertificateによる拇印ではダメでした。(私自身がこの辺りに不慣れなこともあって)様々なオプションを試しましたが、結局、以下の記事を参考にさせていただき、MMCでの「Windows証明書マネージャcertlm」を用いて証明書ファイルServerCert1.pfxをインポートしました。(すべてデフォルトの指定に沿います)
21
+ ### 1. 作成した自己証明書の*.pfxファイルをWindows証明書マネージャでインポート
22
+ 質問者さんの「試したこと1」で、`netsh` コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellの`Get-PfxCertificate`コマンドで得た拇印ではダメでした。(私自身がこの辺りに不慣れなこともあって)様々なオプションを試しましたが、結局、以下の記事を参考にさせていただき、MMCでの「Windows証明書マネージャcertlm」を用いて自己(オレオレ)SSL証明書ファイルServerCert1.pfxをインポートしました。(すべてデフォルトの指定に沿います)
23
23
  [OnTimeサーバーにSSL証明書を設定する - OnTime Group Calendar](https://www3.ontimesuite.jp/ssl-cert/)
24
24
 
25
25
  ### 2. インポートしたWindows証明書マネージャで証明書のサムプリント(拇印)を取得

2

分かりづらい部分を修正

2019/11/11 17:57

投稿

dodox86
dodox86

スコア9416

answer CHANGED
@@ -19,13 +19,13 @@
19
19
  以下、順にご説明します。
20
20
 
21
21
  ### 1. 作成した自己(オレオレ)証明書の*.pfxファイルをWindows証明書マネージャでインポート
22
+ 質問者さんの「試したこと1」で、netsh コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellのGet-PfxCertificateによる拇印ではダメでした。(私自身がこの辺りに不慣れなこともあって)様々なオプションを試しましたが、結局、以下の記事を参考にさせていただき、MMCでの「Windows証明書マネージャcertlm」を用いて証明書ファイルServerCert1.pfxをインポートしました。(すべてデフォルトの指定に沿います)
23
+ [OnTimeサーバーにSSL証明書を設定する - OnTime Group Calendar](https://www3.ontimesuite.jp/ssl-cert/)
24
+
22
25
  ### 2. インポートしたWindows証明書マネージャで証明書のサムプリント(拇印)を取得
23
- 質問者さんの「試したこと1」で、netsh コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellの`Get-PfxCertificate`による拇印ではダメでした。(私自身がこの辺りに不慣れなこともあって)様々なオプションを試しましたが、結局、以下の記事を参考にさせていただき、MMCでの「Windows証明書マネージャ`certlm`」を用いて証明書ファイルServerCert1.pfxをインポートしました。(すべてデフォルトの指定に沿います)証明書のサムプリント(拇印)は、のWindows証明書マネージャで得られたものを利用しました。
26
+ 証明書のサムプリント(拇印)は、1.のWindows証明書マネージャで得られたものを利用しました。 (操作方法は記事中に記載されています
24
27
 
25
- [OnTimeサーバーにSSL証明書を設定する - OnTime Group Calendar](https://www3.ontimesuite.jp/ssl-cert/)
26
-
27
28
  ### 3. `netsh`コマンドでポート構成をバインド
28
-
29
29
  証明書のサムプリントの取得部分が違うだけで、あとは質問者さんで行った方法と同じです。
30
30
  ```CMD
31
31
  netsh http add sslcert ipport=0.0.0.0:5000 certhash=eae70a1d0144a9a367b4aa810f22b8e95815b602 appid={90164bb5-886c-4536-a752-7fbbf652c070} clientcertnegotiation=enable

1

検証結果を追記

2019/11/11 17:48

投稿

dodox86
dodox86

スコア9416

answer CHANGED
@@ -2,4 +2,232 @@
2
2
 
3
3
  "123.123.123.123"は質問投稿用の仮のIPアドレスなのかもしれませんが、いずれにせよlocalhostとは異なります。httpsで運用するサーバーは、アクセスされる際のホスト名を厳密に意識しているはずなので、ここで不一致は良くないのでは。
4
4
 
5
- 開発環境上では同一PCということなので、hostsファイルなどを利用してホスト名の部分を合わせてみるか、IPアドレスを合わせてアクセスしてみてください。あるいは簡単に、クライアント側から "https://localhost:5000/TestService" でアクセスできるのであれば、やはりそういうことなのではないかな、と思います。
5
+ 開発環境上では同一PCということなので、hostsファイルなどを利用してホスト名の部分を合わせてみるか、IPアドレスを合わせてアクセスしてみてください。あるいは簡単に、クライアント側から "https://localhost:5000/TestService" でアクセスできるのであれば、やはりそういうことなのではないかな、と思います。
6
+
7
+ ---
8
+ **追記しました:2019-11-12 02:42**
9
+
10
+ 先の回答は外していました。くやしかった(?)のと、お手を煩わせてしまった罪滅ぼしに当方でも検証してみました。
11
+ 結果から書きますと、以下の処置で限定的ながら通信できるようになりました。
12
+
13
+ 0. 作成した自己(オレオレ)証明書の*.pfxファイルをWindows証明書マネージャでインポート
14
+ 0. インポートしたWindows証明書マネージャで証明書のサムプリント(拇印)を取得
15
+ 0. `netsh`コマンドでポート構成をバインド
16
+ 0. サーバー側プログラムで、匿名アクセスを受け付けるよう修正
17
+ 0. クライアント側プログラムで、「自己証明書による不正な証明書エラー」によるエラーを回避するよう修正
18
+
19
+ 以下、順にご説明します。
20
+
21
+ ### 1. 作成した自己(オレオレ)証明書の*.pfxファイルをWindows証明書マネージャでインポート
22
+ ### 2. インポートしたWindows証明書マネージャで証明書のサムプリント(拇印)を取得
23
+ 質問者さんの「試したこと1」で、netsh コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellの`Get-PfxCertificate`による拇印ではダメでした。(私自身がこの辺りに不慣れなこともあって)様々なオプションを試しましたが、結局、以下の記事を参考にさせていただき、MMCでの「Windows証明書マネージャ`certlm`」を用いて証明書ファイルServerCert1.pfxをインポートしました。(すべてデフォルトの指定に沿います)証明書のサムプリント(拇印)は、このWindows証明書マネージャで得られたものを利用しました。
24
+
25
+ [OnTimeサーバーにSSL証明書を設定する - OnTime Group Calendar](https://www3.ontimesuite.jp/ssl-cert/)
26
+
27
+ ### 3. `netsh`コマンドでポート構成をバインド
28
+
29
+ 証明書のサムプリントの取得部分が違うだけで、あとは質問者さんで行った方法と同じです。
30
+ ```CMD
31
+ netsh http add sslcert ipport=0.0.0.0:5000 certhash=eae70a1d0144a9a367b4aa810f22b8e95815b602 appid={90164bb5-886c-4536-a752-7fbbf652c070} clientcertnegotiation=enable
32
+ ```
33
+
34
+ `netsh http show sslcert`でSSL証明書のバインド状況を確認すると、以下のようなものとなりました。
35
+ ```CMD
36
+ C> netsh http show sslcert
37
+ SSL 証明書のバインド:
38
+ -------------------------
39
+
40
+
41
+
42
+ IP:ポート : 0.0.0.0:5000
43
+ 証明書ハッシュ : eae70a1d0144a9a367b4aa810f22b8e95815b602
44
+ アプリケーション ID : {90164bb5-886c-4536-a752-7fbbf652c070}
45
+ 証明書ストア名 : (null)
46
+ クライアント証明書の失効状態の検証: Enabled
47
+ キャッシュされたクライアント証明書のみを使用した失効状態の検証: Disabled
48
+ 使用法のチェック : Enabled
49
+ 失効リストの更新を確認する間隔: 0
50
+ URL 取得のタイムアウト : 0
51
+ Ctl 識別子 : (null)
52
+ Ctl ストア名 : (null)
53
+ DS マッパーの使用法 : Disabled
54
+ クライアント証明書のネゴシエート: Enabled
55
+ 接続の拒否 : Disabled
56
+ HTTP2 を無効にする : Not Set
57
+ QUIC を無効にする : Not Set
58
+ TLS1.2 を無効にする : Not Set
59
+ TLS1.3 を無効にする : Not Set
60
+ OCSP Stapling を無効にする : Not Set
61
+ トークンのバインドを有効にする : Not Set
62
+ 拡張イベントをログに記録する : Not Set
63
+ レガシ TLS バージョンを無効にする : Not Set
64
+ セッション チケットを有効にする : Not Set
65
+ 拡張プロパティ:
66
+ PropertyId : 0
67
+ 受信ウィンドウ : 1048576
68
+ 拡張プロパティ:
69
+ PropertyId : 1
70
+ フレームごとの最大設定 : 2796202
71
+ 1 分間の最大設定 : 4294967295
72
+ ```
73
+ ### 4. クライアント側プログラムで、「自己証明書による不正な証明書エラー」によるエラーを回避するよう修正
74
+
75
+ もとのサーバー、クライアントプログラムですと、自己証明書を使っていることによる、`System.ServiceModel.Security.SecurityNegotiationException` 「リモート証明書は無効です。」例外が起きます。
76
+ ```
77
+ System.ServiceModel.Security.SecurityNegotiationException
78
+ HResult=0x80131501
79
+ Message=機関 'servercn1:5000' との SSL/TLS のセキュリティで保護されているチャネルに対する信頼関係を確立できませんでした。
80
+ Source=mscorlib
81
+ スタック トレース:
82
+ ...(省略)
83
+
84
+ 内部例外 1:
85
+ WebException: 接続が切断されました: SSL/TLS のセキュリティで保護されているチャネルに対する信頼関係を確立できませんでした。
86
+
87
+ 内部例外 2:
88
+ AuthenticationException: 検証プロシージャによると、リモート証明書は無効です。
89
+ ```
90
+
91
+ これを避けるために、クライアント側に自己証明書であった場合でも、無理やりOKとするよう修正します。
92
+ 参考:[How to accept a self-signed SSL certificate in a WCF client? - Stack Overflow](https://stackoverflow.com/questions/4977218/how-to-accept-a-self-signed-ssl-certificate-in-a-wcf-client)
93
+
94
+ ```C#
95
+ // MessageData rtn = channel.PostMsg(msg); で送信する前に、リモート証明書の妥当性チェックコールバックを登録
96
+ ServicePointManager.ServerCertificateValidationCallback +=
97
+ new System.Net.Security.RemoteCertificateValidationCallback(EasyCertCheck);
98
+
99
+ // コールバックメソッド
100
+ bool EasyCertCheck(object sender, X509Certificate cert,
101
+ X509Chain chain, System.Net.Security.SslPolicyErrors error)
102
+ {
103
+ // 自己証明書の場合でも必ずチェックOKとする
104
+ Debug.WriteLine("EasyCertCheck");
105
+ return true;
106
+ }
107
+ ```
108
+ ### 5. サーバー側プログラムで、匿名アクセスを受け付けるよう修正
109
+
110
+ サーバー側のコードで、`binding.Security.Transport.ClientCredentialType`が`HttpClientCredentialType.Certificate`であると、以下の例外となります。HTTPステータス403なので一応は繋がっているかたちですが、成功としてレスポンスを受信したいので、この設定値を緩やかなものに変えます。
111
+ ```
112
+ System.ServiceModel.Security.MessageSecurityException
113
+ HResult=0x80131501
114
+ Message=この HTTP 要求は、クライアントの認証方式 'Anonymous' で許可されませんでした。
115
+ Source=WindowsFormsApp1
116
+ スタック トレース:
117
+ ...(省略)
118
+
119
+ 内部例外 1:
120
+ WebException: リモート サーバーがエラーを返しました: (403) 使用不可能
121
+ ```
122
+
123
+ ```C#
124
+ //ここを無効にして binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
125
+
126
+ // 匿名アクセスを可能とする
127
+ binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
128
+ ```
129
+
130
+ ### 6. まとめ
131
+ 自己証明書用でも構わず処理するようにクライアントを修正したのと、特に匿名アクセスでも受け付けるようにサーバーを修正した点は気になるところですが、一応https接続はできるようになったので、現状の回答として追記させてもらった次第です。
132
+
133
+ 検証した環境は以下となります。。
134
+ - Windows 10 Professional Version 1903 (OSビルド 18362.418)
135
+ - Visual Studio 2017 (Version 15.9.17)
136
+ - .NET Framework 4.5
137
+
138
+ プログラムの実行状況です。
139
+ ![プログラム実行状況](2bf0fad0b793d3ac094fbd74a2fc5b52.png)
140
+
141
+ クライアント側はProgram.csの修正量が多いので、全部掲載します。
142
+ ```C#
143
+ using System;
144
+ using System.Windows.Forms;
145
+ using System.Security.Cryptography.X509Certificates;
146
+ using System.ServiceModel;
147
+ using System.ServiceModel.Description;
148
+ using System.ServiceModel.Security;
149
+ using System.ServiceModel.Web;
150
+ using System.Net;
151
+ using System.Diagnostics;
152
+ using TestService;
153
+
154
+ namespace TestClient
155
+ {
156
+ public partial class Form1 : Form
157
+ {
158
+ WebChannelFactory<ITestService> cf = null;
159
+ ITestService channel = null;
160
+ //private static string pfxPath = @"D:\Work\TestService\ServerCert1.pfx";
161
+ private static string pfxPath = @"C:\project\teratail\221557\ServerCert1.pfx";
162
+ //private static string uris = @"https://123.123.123.123:5000/TestService";
163
+ //private static string uris = @"https://localhost:5000/TestService";
164
+ //private static string uris = @"https://ServerCN1:5000/TestService";
165
+ private static string uris = @"https://192.168.11.103:5000/TestService";
166
+
167
+ public Form1()
168
+ {
169
+ InitializeComponent();
170
+ }
171
+
172
+ private void Form1_Load(object sender, EventArgs e)
173
+ {
174
+ listBox1.HorizontalScrollbar = true;
175
+
176
+ //Uri uri = new Uri("https://123.123.123.123:5000/TestService");
177
+ Uri uri = new Uri(uris);
178
+ EndpointAddress endpointAddress = new EndpointAddress(uri);
179
+
180
+ //--------------------------チャンネル
181
+ cf = new WebChannelFactory<ITestService>(uri);
182
+ WebHttpBinding binding = cf.Endpoint.Binding as WebHttpBinding;
183
+ binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
184
+ binding.Security.Mode = WebHttpSecurityMode.Transport;
185
+
186
+ var behavior = new WebHttpBehavior();
187
+ behavior.FaultExceptionEnabled = false;
188
+ behavior.HelpEnabled = true;
189
+ behavior.DefaultOutgoingRequestFormat = WebMessageFormat.Json;
190
+ behavior.DefaultOutgoingResponseFormat = WebMessageFormat.Json;
191
+ cf.Endpoint.Behaviors.Add(behavior);
192
+
193
+ ServicePointManager.ServerCertificateValidationCallback +=
194
+ new System.Net.Security.RemoteCertificateValidationCallback(EasyCertCheck);
195
+
196
+ //var clientCertificate = new X509Certificate2(@"D:\Work\TestService\ServerCert1.pfx", "pswd", X509KeyStorageFlags.UserKeySet);
197
+ var clientCertificate = new X509Certificate2(pfxPath, "pswd", X509KeyStorageFlags.UserKeySet);
198
+ cf.Credentials.ClientCertificate.Certificate = clientCertificate;
199
+ cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
200
+ //cf.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.PeerTrust;
201
+
202
+ channel = cf.CreateChannel();
203
+ }
204
+
205
+ private void button1_Click(object sender, EventArgs e)
206
+ {
207
+ MessageData msg = new MessageData()
208
+ {
209
+ Name = "Taro",
210
+ Gender = 1,
211
+ Age = 3
212
+ };
213
+ try
214
+ {
215
+ MessageData rtn = channel.PostMsg(msg); //<--------ここでエラー発生
216
+ listBox1.Items.Insert(0, string.Format("Name:{0}, Gender:{1}, Age{2} ", rtn.Name, rtn.Gender, rtn.Age));
217
+ }
218
+ catch (Exception ex)
219
+ {
220
+ Trace.WriteLine(ex.Message);
221
+ throw ex;
222
+ }
223
+ }
224
+
225
+ private bool EasyCertCheck(object sender, X509Certificate cert,
226
+ X509Chain chain, System.Net.Security.SslPolicyErrors error)
227
+ {
228
+ Debug.WriteLine("EasyCertCheck");
229
+ return true;
230
+ }
231
+ }
232
+ }
233
+ ```