回答編集履歴
3
一部、表現を修正
answer
CHANGED
@@ -10,7 +10,7 @@
|
|
10
10
|
先の回答は外していました。くやしかった(?)のと、お手を煩わせてしまった罪滅ぼしに当方でも検証してみました。
|
11
11
|
結果から書きますと、以下の処置で限定的ながら通信できるようになりました。
|
12
12
|
|
13
|
-
0. 作成した自己
|
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. 作成した自己
|
22
|
-
質問者さんの「試したこと1」で、netsh コマンドによるSSL証明書のポート構成のバインド操作ですが、PowerShellのGet-PfxCertificate
|
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
分かりづらい部分を修正
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
|
-
|
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
検証結果を追記
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
|
+

|
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
|
+
```
|