いやぁ、かなりのご無沙汰。1年以上書いてないとは。。w
書くネタについては溢れんばかりにあるけれど、書くのが面倒で書いてない。。。つくづくこういう行為があっていないのだなぁと思います。
さて、タイトルにあるようにC#でSSL通信のSOAPを利用しようとしたら、めっちゃ躓いたので、備忘録。
まず、クライアント認証について。
クライアント認証は、ものすごく簡単に説明すると、リバースプロキシによる実サービスへのディスパッチ時に証明書による認証を要するもの、って認識です。間違っていたらすんません。
で、公開されているサービスをクライアント認証に対応させるために、どうやったら良いのかが分からず、最終的にはMicrosoftの有償チケット使ってしまった。。。(実際にはネット上に情報は溢れていたんだけど。。。)さらに、自分の用途では、通常のHTTPでのサービスとリバースプロキシ経由HTTPSでのサービスを切り替える必要があったので、さらに難解(当者比)になってしまった。
基本的にクライアント認証は、Windowsの証明書に組み込まれたものを利用する。まずはこの証明書を取得することが必要となる。取得するには、下記のコードを準備する。元ネタはここ。
public static X509Certificate2 GetCertificateFromStore(string certSubjectName)
{
X509Store store = new X509Store(StoreLocation.CurrentUser);
try
{
store.Open(OpenFlags.ReadOnly);
X509Certificate2Collection certCollection = store.Certificates;
X509Certificate2Collection currentCerts = certCollection.Find(X509FindType.FindByTimeValid, DateTime.Now, false);
X509Certificate2Collection signingCert = currentCerts.Find(X509FindType.FindBySubjectDistinguishedName, certSubjectName, false);
if (signingCert.Count == 0)
return null;
return signingCert[0];
}
finally
{
store.Close();
}
}
コード内の「X509FindType.FindBySubjectDistinguishedName」は色々種類があるのだけれど、証明書内のどの項目で探すのかによって変更する。staticかどうかは、まぁ、好み。これによって得られたクライアント証明書をこの後の設定で利用する。
次に、VisualStudioのサービス参照追加でサービス用のProxy Classを作成した場合、以下のように証明書を添付する。
service.ClientCredentials.ClientCertificate.Certificate = cert;
「service」はProxy Classのインスタンス、「cert」は上記で取得したX509Certificate2。これにより、クライアント認証はOK。
で、通常のSSLで公開されているサービスであれば、証明書はちゃんとしたものであるので、サービス参照追加で作成が(多分)可能なんだけど、今回はテスト環境なのでオレオレ証明書。そうなるとサービス参照追加でSSL/TLSの確率ができねぇ!って言われて、ちゃんと作成できない。証明書を登録しても、SANを追加してもダメ。。。
なので、まずはサービスの実体を使ってProxy Classを作成する。その上で、app.configに追加されたEndpointをリバースプロキシのURLに変更。これにより、SSLの通信になって、且つ、クライアント認証もできるはず。。。と思ったら、出来なかった。。。。エラーとして、以下のようなものがでる。
System.ArgumentException: '指定された URI 形式 'https' は無効です。有効な URI は 'http' です。
パラメーター名:via'
で、諸々調べたら、app.configのBinding情報に以下の追加が必要だった。(これ調べるのに時間掛かった。。。orz)
<binding name="HogeSoap11Binding">
<security mode="Transport">
<message clientCredentialType="Certificate" algorithmSuite="Default"/>
<transport clientCredentialType="Certificate" proxyCredentialType="Basic" />
</security>
</binding>
追加する部分は<security>の部分だけど、この中身についてはSSLの実装内容によって変わるみたい。(未検証)で、この項目はコードからも追加できる。
var binding = (System.ServiceModel.BasicHttpBinding)SessionValues.users.ChannelFactory.Endpoint.Bindingbinding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;
binding.Security.Message.AlgorithmSuite = System.ServiceModel.Security.SecurityAlgorithmSuite.Default;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
binding.Security.Transport.ProxyCredentialType = HttpProxyCredentialType.Basic;
これにより、SSLでの通信ができて、クライアント認証もできることになる。
さらに、HTTPとHTTPSを切り替える、というか、コード内でEndpointのURLを変更するには、下記のコードでできる。
Hoge.HogePortTypeClient("HogeHttpSoap11Endpoint", new EndpointAddress("https://hogehogte.com/~~~"));
これにより、任意のURLにEndpointを切り替えられる。
ふぅ〜久しぶりに長文を書いた。足りない部分もあるけれど、備忘録としては十分。ではでは〜。