WCF net.tcp с ssl
Ну, я пытаюсь настроить соединение net.tcp с шифрованием SSL У меня есть следующие классы
Интерфейс Wcf:
[ServiceContract()]
public interface IServer
{
[OperationContract]
void Send(string message);
}
Сервер:
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Multiple, UseSynchronizationContext = false)]
class Server : IServer
{
readonly ServiceHost host;
public Server()
{
host = new ServiceHost(this);
host.AddServiceEndpoint(typeof(IServer), Program.GetBinding(), string.Format("net.tcp://localhost:{0}/", 1010));
host.Credentials.ServiceCertificate.Certificate = Program.LoadCert();
host.Open();
}
public void Send(string message)
{
Console.WriteLine(message);
}
}
Программа (Клиент / Сервер):
class Program
{
static ChannelFactory<IServer> channelFactory;
static IServer server;
static RemoteCertificateValidationCallback callback = null;
public const int MaxMessageSize = 1024 * 1024 * 2;
public static Binding GetBinding()
{
NetTcpBinding binding = new NetTcpBinding();
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Message.ClientCredentialType = MessageCredentialType.None;
binding.ReaderQuotas.MaxArrayLength = MaxMessageSize;
binding.ReaderQuotas.MaxBytesPerRead = MaxMessageSize;
binding.MaxBufferSize = MaxMessageSize;
binding.MaxReceivedMessageSize = MaxMessageSize;
binding.MaxBufferPoolSize = binding.MaxBufferSize * 10;
binding.TransactionFlow = false;
binding.ReliableSession.Enabled = false;
binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
return binding;
}
public static X509Certificate2 LoadCert()
{
X509Certificate2 cert = new X509Certificate2();
cert.Import(@"Test2.pfx", "Test", X509KeyStorageFlags.DefaultKeySet);
return cert;
}
static void Main(string[] args)
{
try
{
callback = new RemoteCertificateValidationCallback(ValidateCertificate);
ServicePointManager.ServerCertificateValidationCallback += callback;
Console.WriteLine("[C]lient or [S]erver?");
ConsoleKeyInfo key = Console.ReadKey(true);
if (key.Key == ConsoleKey.S)
{
StartServer();
}
else if (key.Key == ConsoleKey.C)
{
StartClient();
}
}
finally
{
if (callback != null)
ServicePointManager.ServerCertificateValidationCallback -= callback;
}
}
private static void StartClient()
{
Console.WriteLine("Starting client mode!");
Console.Write("Host:");
string host = Console.ReadLine();
channelFactory = new ChannelFactory<IServer>(GetBinding());
server = channelFactory.CreateChannel(new EndpointAddress(string.Format("net.tcp://{0}:{1}/", host, 1010)));
while (true)
{
Console.Write("Message:");
server.Send(Console.ReadLine());
}
}
private static void StartServer()
{
Console.WriteLine("Starting server mode!");
Server server = new Server();
Console.ReadKey();
GC.KeepAlive(server);
}
public static bool ValidateCertificate(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
Console.WriteLine("ValidateCertificate");
return true;
}
}
Это прекрасно работает, когда сервер и клиент работают на одном компьютере (но ValidateCertificate никогда не вызывается так, как должно быть).
Но если я запускаю их на разных компьютерах, я получаю следующее исключение на клиенте:
Описание: процесс был прерван из-за необработанного исключения. Информация об исключении: System.ServiceModel.Security.SecurityNegotiationException Stack:
Стек сервера трассировки: в System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.OnInitiateUpgrade(поток поток, SecurityMessageProperty& remoteSecurity) в System.ServiceModel.Channels.StreamSecurityUpgradeInitiatorBase.InitiateUpgrade(поток) в поток System.ServiceModel.Channels.ConnectionUpgradeHelper.InitiateUpgrade(StreamUpgradeInitiator upgradeInitiator, IConnection& connection, декодер ClientFramingDecoder, IDefaultCommunicationTimeouts defaultTimeouts, TimeoutHelper& timeoutHelper) в System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.SendPreamble (соединение IConnection, timerayHerperHerperHerHelperHerperHerperHerperHerperHerperHerperHerperHerperHergherperHerperHerperHermentHerHerperHer для HAM)
в System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.DuplexConnectionPoolHelper.AcceptPooledConnection (IConnection связи, TimeoutHelper и timeoutHelper) в System.ServiceModel.Channels.ConnectionPoolHelper.EstablishConnection(тайм-аут TimeSpan) при System.ServiceModel.Channels.ClientFramingDuplexSessionChannel.OnOpen(тайм-аут TimeSpan) в системе.ServiceModel.Channels.CommunicationObject.Open(тайм-аут TimeSpan) в System.ServiceModel.Channels.ServiceChannel.OnOpen(тайм-аут TimeSpan)
в System.ServiceModel.Channels.CommunicationObject.Open(тайм-аут TimeSpan) в System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(канал ServiceChannelShannelShannelShannel в момент времени).ServiceChannel.CallOnceManager.CallOnce(тайм-аут TimeSpan, каскад CallOnceManager) в System.ServiceModel.Channels.ServiceChannel.Call(строковое действие, логический односторонний метод, операция ProxyOperationRuntime, Object[] ins, Object[] outs, TimeSpanServiceMervice.Sime) в System.Sec.Channels.ServiceChannelProxy.InvokeService(метод IMethodCallMessageCall, операция ProxyOperationRuntime) в System.ServiceModel.Channels.ServiceChannelProxy.Invoke(сообщение IMessage) в System.Runtime.Remoting.Proxies.Real.MessageRealProxy..Runtime.Remoting.Messaging.IMessage) в System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(System.Runtime.Remoting.Proxies.MessageData ByRef, Int32) в WcfChatTest.Client.Program+IServer.Send(System.String) в WcfChatTest.Client.Program.StartClient() в WcfChatTest.Client.Program.Main(System.String[])
Что я настроил неправильно, и почему он вызывает "WindowsStreamSecurityUpgradeProvider", когда я предоставил ему сертификат?
Или, если у кого-то есть отличный пример, как получить шифрование транспорта в net.tcp, если клиент и сервер не находятся в одном домене?
1 ответ
Я решил проблему на линии
server = channelFactory.CreateChannel(new EndpointAddress(string.Format("net.tcp://{0}:{1}/", host, 1010)));
мне пришлось добавить дополнительный параметр, который я создал с помощью следующего кода
EndpointIdentity.CreateX509CertificateIdentity(certificate)