WCFで NetTcpBinding を使った通信を SSL over TCPで暗号化しているときに、暗号化に使っているサーバー側の証明書をクライアント側で取得したくなった。主な目的は通信テスト。
SslStream で最初いけるかと思ったけど、即サーバー側から切断されてしまい、解決できず断念。別のアプローチを探したところ、WCFでの証明書の検証にカスタム証明書バリデーターを使うことで実現できた。
using System; using System.IdentityModel.Selectors; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; using System.ServiceModel; using System.ServiceModel.Security; namespace SslCertSample { internalclassProgram { staticvoid Main(string[] args) { var hostname = args[0]; var port =int.Parse(args[1]); var binding =new NetTcpBinding(); binding.Security.Mode = SecurityMode.Transport; binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.None; binding.Security.Transport.SslProtocols = SslProtocols.Tls12; var address =new EndpointAddress( uri:new Uri($"net.tcp://{hostname}:{port}/GreetingService"), identity: EndpointIdentity.CreateDnsIdentity("your-domain.com")); var certificateValidator =new MyCertificateValidator(); var factory =new ChannelFactory<IGreetingService>( binding: binding, remoteAddress: address); factory.Credentials.ServiceCertificate.SslCertificateAuthentication =new X509ServiceCertificateAuthentication { // 証明書の検証をカスタムにして、// サーバー証明書を取り出すためだけのカスタム証明書バリデーターを使う CertificateValidationMode = X509CertificateValidationMode.Custom, CustomCertificateValidator = certificateValidator, }; var channel = factory.CreateChannel(); var result = channel.Hello("Kubo"); WriteCertificate(certificateValidator.Certificate); Console.WriteLine(); foreach (var element in certificateValidator.Chain?.ChainElements) { WriteCertificate(element.Certificate); Console.WriteLine(); } Console.WriteLine("Enter で終了"); Console.ReadLine(); } privatestaticvoid WriteCertificate(X509Certificate2 certificate) { Console.WriteLine($"Subject: {certificate?.Subject}"); Console.WriteLine($"SubjectName: {certificate?.SubjectName.Name}"); Console.WriteLine($"IssuerName: {certificate?.IssuerName.Name}"); } } // サーバー証明書を取り出すためだけのカスタム証明書バリデーターpublicclassMyCertificateValidator : X509CertificateValidator, IDisposable { public X509Certificate2 Certificate { get; privateset; } public X509Chain Chain { get; privateset; } publicoverridevoid Validate(X509Certificate2 certificate) { Certificate = certificate; Chain =new X509Chain(useMachineContext:false) { ChainPolicy =new X509ChainPolicy { // 証明書チェーンを取得するだけなので、チェックしない RevocationMode = X509RevocationMode.NoCheck, }, }; Chain.Build(certificate); } publicvoid Dispose() { Chain?.Dispose(); } } [ServiceContract] publicinterface IGreetingService { [OperationContract] string Hello(string name); } }