Я пытаюсь создать простое соединение TCP (TLS1.2) TCP.Я использую самоподписанный сертификат, сгенерированный кодом.Я продолжаю получать сообщение об ошибке.
Сбой вызова SSPI, см. Внутреннее исключение.---> System.ComponentModel.Win32Exception: клиент и сервер не могут обмениваться данными, поскольку у них нет общего алгоритма
Ниже приводится мой полный код .net 4.7.1.:
using System;
using System.IO;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Authentication;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Threading;
namespace SSLStreamBasic
{
internal class Program
{
private static void Main()
{
SecureTcpServer server = null;
SecureTcpClient client = null;
try
{
int port = 8889;
RemoteCertificateValidationCallback certValidationCallback = IgnoreCertificateErrorsCallback;
X509Certificate serverCert = Constants.PrivateKey;
server = new SecureTcpServer(port, serverCert,
OnServerConnectionAvailable);
server.StartListening();
client = new SecureTcpClient(OnClientConnectionAvailable,
certValidationCallback);
client.StartConnecting("localhost", new IPEndPoint(IPAddress.Loopback, port));
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
//sleep to avoid printing this text until after the callbacks have been invoked.
Thread.Sleep(4000);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
if (server != null)
server.Dispose();
if (client != null)
client.Dispose();
}
private static void OnServerConnectionAvailable(object sender, SecureConnectionResults args)
{
if (args.AsyncException != null)
{
Console.WriteLine(args.AsyncException);
return;
}
SslStream stream = args.SecureStream;
Console.WriteLine("Server Connection secured: " + stream.IsAuthenticated);
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = true;
writer.WriteLine("Hello from server!");
StreamReader reader = new StreamReader(stream);
string line = reader.ReadLine();
Console.WriteLine("Server Recieved: '{0}'", line == null ? "<NULL>" : line);
writer.Close();
reader.Close();
stream.Close();
}
private static void OnClientConnectionAvailable(object sender, SecureConnectionResults args)
{
if (args.AsyncException != null)
{
Console.WriteLine(args.AsyncException);
return;
}
SslStream stream = args.SecureStream;
Console.WriteLine("Client Connection secured: " + stream.IsAuthenticated);
StreamWriter writer = new StreamWriter(stream);
writer.AutoFlush = true;
writer.WriteLine("Hello from client!");
StreamReader reader = new StreamReader(stream);
string line = reader.ReadLine();
Console.WriteLine("Client Recieved: '{0}'", line == null ? "<NULL>" : line);
writer.Close();
reader.Close();
stream.Close();
}
private static bool IgnoreCertificateErrorsCallback(object sender,
X509Certificate certificate,
X509Chain chain,
SslPolicyErrors sslPolicyErrors)
{
if (sslPolicyErrors != SslPolicyErrors.None)
{
Console.WriteLine("IgnoreCertificateErrorsCallback: {0}", sslPolicyErrors);
//you should implement different logic here...
if ((sslPolicyErrors & SslPolicyErrors.RemoteCertificateChainErrors) != 0)
{
foreach (X509ChainStatus chainStatus in chain.ChainStatus)
{
Console.WriteLine("\t" + chainStatus.Status);
}
}
}
//returning true tells the SslStream object you don't care about any errors.
return true;
}
}
public class SecureTcpClient : IDisposable
{
private X509CertificateCollection _clientCertificates;
private readonly RemoteCertificateValidationCallback _certValidationCallback;
private readonly SecureConnectionResultsCallback _connectionCallback;
private bool _checkCertificateRevocation;
private readonly AsyncCallback _onConnected;
private readonly AsyncCallback _onAuthenticateAsClient;
private TcpClient _client;
private IPEndPoint _remoteEndPoint;
private string _remoteHostName;
private readonly SslProtocols _protocols;
private int _disposed;
public SecureTcpClient(SecureConnectionResultsCallback callback)
: this(callback, null, SslProtocols.Tls12)
{
}
public SecureTcpClient(SecureConnectionResultsCallback callback,
RemoteCertificateValidationCallback certValidationCallback)
: this(callback, certValidationCallback, SslProtocols.Tls12)
{
}
public SecureTcpClient(SecureConnectionResultsCallback callback,
RemoteCertificateValidationCallback certValidationCallback, SslProtocols sslProtocols)
{
if (callback == null)
throw new ArgumentNullException("callback");
_onConnected = OnConnected;
_onAuthenticateAsClient = OnAuthenticateAsClient;
this._certValidationCallback = certValidationCallback;
_connectionCallback = callback;
_protocols = sslProtocols;
_disposed = 0;
}
~SecureTcpClient()
{
Dispose();
}
public bool CheckCertificateRevocation
{
get { return _checkCertificateRevocation; }
set { _checkCertificateRevocation = value; }
}
public void StartConnecting(string remoteHostName, IPEndPoint remoteEndPoint)
{
StartConnecting(remoteHostName, remoteEndPoint, null);
}
public void StartConnecting(string remoteHostName, IPEndPoint remoteEndPoint,
X509CertificateCollection clientCertificates)
{
if (string.IsNullOrEmpty(remoteHostName))
throw new ArgumentException("Value cannot be null or empty", "remoteHostName");
if (remoteEndPoint == null)
throw new ArgumentNullException("remoteEndPoint");
Console.WriteLine("Client connecting to: {0}", remoteEndPoint);
this._clientCertificates = clientCertificates;
this._remoteHostName = remoteHostName;
this._remoteEndPoint = remoteEndPoint;
if (_client != null)
_client.Close();
_client = new TcpClient(remoteEndPoint.AddressFamily);
_client.BeginConnect(remoteEndPoint.Address,
remoteEndPoint.Port,
_onConnected, null);
}
public void Close()
{
Dispose();
}
private void OnConnected(IAsyncResult result)
{
SslStream sslStream = null;
try
{
bool leaveStreamOpen = false;//close the socket when done
if (_certValidationCallback != null)
sslStream = new SslStream(_client.GetStream(), leaveStreamOpen, _certValidationCallback);
else
sslStream = new SslStream(_client.GetStream(), leaveStreamOpen);
sslStream.BeginAuthenticateAsClient(_remoteHostName,
_clientCertificates,
_protocols,
_checkCertificateRevocation,
_onAuthenticateAsClient,
sslStream);
}
catch (Exception ex)
{
if (sslStream != null)
{
sslStream.Dispose();
sslStream = null;
}
_connectionCallback(this, new SecureConnectionResults(ex));
}
}
private void OnAuthenticateAsClient(IAsyncResult result)
{
SslStream sslStream = null;
try
{
sslStream = result.AsyncState as SslStream;
sslStream.EndAuthenticateAsClient(result);
_connectionCallback(this, new SecureConnectionResults(sslStream));
}
catch (Exception ex)
{
if (sslStream != null)
{
sslStream.Dispose();
sslStream = null;
}
_connectionCallback(this, new SecureConnectionResults(ex));
}
}
public void Dispose()
{
if (Interlocked.Increment(ref _disposed) == 1)
{
if (_client != null)
{
_client.Close();
_client = null;
}
GC.SuppressFinalize(this);
}
}
}
public static class Constants
{
public static X509Certificate2 PrivateKey => CreateCertificate();
public static X509Certificate2 CreateCertificate()
{
var ecdsa = ECDsa.Create(); // generate asymmetric key pair
var req = new CertificateRequest("CN=foobar", ecdsa, HashAlgorithmName.SHA512);
var cert = req.CreateSelfSigned(DateTimeOffset.Now, DateTimeOffset.Now.AddYears(5));
// Create PFX (PKCS #12) with private key
var x = cert.Export(X509ContentType.Pfx, "123456789");
var cer = new X509Certificate2(x, "123456789");
return cer;
}
}
public delegate void SecureConnectionResultsCallback(object sender, SecureConnectionResults args);
public class SecureConnectionResults
{
private readonly SslStream _secureStream;
private readonly Exception _asyncException;
internal SecureConnectionResults(SslStream sslStream)
{
_secureStream = sslStream;
}
internal SecureConnectionResults(Exception exception)
{
_asyncException = exception;
}
public Exception AsyncException { get { return _asyncException; } }
public SslStream SecureStream { get { return _secureStream; } }
}
public class SecureTcpServer : IDisposable
{
private readonly X509Certificate _serverCert;
private readonly RemoteCertificateValidationCallback _certValidationCallback;
private readonly SecureConnectionResultsCallback _connectionCallback;
private readonly AsyncCallback _onAcceptConnection;
private readonly AsyncCallback _onAuthenticateAsServer;
private bool _started;
private readonly int _listenPort;
private TcpListener _listenerV4;
private TcpListener _listenerV6;
private int _disposed;
private bool _clientCertificateRequired;
private bool _checkCertifcateRevocation;
private SslProtocols _sslProtocols;
public SecureTcpServer(int listenPort, X509Certificate serverCertificate,
SecureConnectionResultsCallback callback)
: this(listenPort, serverCertificate, callback, null)
{
}
public SecureTcpServer(int listenPort, X509Certificate serverCertificate,
SecureConnectionResultsCallback callback,
RemoteCertificateValidationCallback certValidationCallback)
{
if (listenPort < 0 || listenPort > UInt16.MaxValue)
throw new ArgumentOutOfRangeException("listenPort");
if (serverCertificate == null)
throw new ArgumentNullException("serverCertificate");
if (callback == null)
throw new ArgumentNullException("callback");
_onAcceptConnection = OnAcceptConnection;
_onAuthenticateAsServer = OnAuthenticateAsServer;
_serverCert = serverCertificate;
this._certValidationCallback = certValidationCallback;
_connectionCallback = callback;
this._listenPort = listenPort;
_disposed = 0;
_checkCertifcateRevocation = false;
_clientCertificateRequired = false;
_sslProtocols = SslProtocols.Tls12;
}
~SecureTcpServer()
{
Dispose();
}
public SslProtocols SslProtocols
{
get { return _sslProtocols; }
set { _sslProtocols = value; }
}
public bool CheckCertifcateRevocation
{
get { return _checkCertifcateRevocation; }
set { _checkCertifcateRevocation = value; }
}
public bool ClientCertificateRequired
{
get { return _clientCertificateRequired; }
set { _clientCertificateRequired = value; }
}
public void StartListening()
{
if (_started)
throw new InvalidOperationException("Already started...");
IPEndPoint localIp;
if (Socket.SupportsIPv4 && _listenerV4 == null)
{
localIp = new IPEndPoint(IPAddress.Any, _listenPort);
Console.WriteLine("SecureTcpServer: Started listening on {0}", localIp);
_listenerV4 = new TcpListener(localIp);
}
if (Socket.OSSupportsIPv6 && _listenerV6 == null)
{
localIp = new IPEndPoint(IPAddress.IPv6Any, _listenPort);
Console.WriteLine("SecureTcpServer: Started listening on {0}", localIp);
_listenerV6 = new TcpListener(localIp);
}
if (_listenerV4 != null)
{
_listenerV4.Start();
_listenerV4.BeginAcceptTcpClient(_onAcceptConnection, _listenerV4);
}
if (_listenerV6 != null)
{
_listenerV6.Start();
_listenerV6.BeginAcceptTcpClient(_onAcceptConnection, _listenerV6);
}
_started = true;
}
public void StopListening()
{
if (!_started)
return;
_started = false;
if (_listenerV4 != null)
_listenerV4.Stop();
if (_listenerV6 != null)
_listenerV6.Stop();
}
private void OnAcceptConnection(IAsyncResult result)
{
TcpListener listener = result.AsyncState as TcpListener;
TcpClient client;
SslStream sslStream = null;
try
{
if (_started)
{
//start accepting the next connection...
listener.BeginAcceptTcpClient(_onAcceptConnection, listener);
}
else
{
//someone called Stop() - don't call EndAcceptTcpClient because
//it will throw an ObjectDisposedException
return;
}
//complete the last operation...
client = listener.EndAcceptTcpClient(result);
bool leaveStreamOpen = false;//close the socket when done
if (_certValidationCallback != null)
sslStream = new SslStream(client.GetStream(), leaveStreamOpen, _certValidationCallback);
else
sslStream = new SslStream(client.GetStream(), leaveStreamOpen);
sslStream.BeginAuthenticateAsServer(_serverCert,
_clientCertificateRequired,
_sslProtocols,
_checkCertifcateRevocation,//checkCertifcateRevocation
_onAuthenticateAsServer,
sslStream);
}
catch (Exception ex)
{
if (sslStream != null)
{
sslStream.Dispose();
sslStream = null;
}
_connectionCallback(this, new SecureConnectionResults(ex));
}
}
private void OnAuthenticateAsServer(IAsyncResult result)
{
SslStream sslStream = null;
try
{
sslStream = result.AsyncState as SslStream;
sslStream.EndAuthenticateAsServer(result);
_connectionCallback(this, new SecureConnectionResults(sslStream));
}
catch (Exception ex)
{
if (sslStream != null)
{
sslStream.Dispose();
sslStream = null;
}
_connectionCallback(this, new SecureConnectionResults(ex));
}
}
public void Dispose()
{
if (Interlocked.Increment(ref _disposed) == 1)
{
if (_listenerV4 != null)
_listenerV4.Stop();
if (_listenerV6 != null)
_listenerV6.Stop();
_listenerV4 = null;
_listenerV6 = null;
GC.SuppressFinalize(this);
}
}
}
}