diff --git a/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/Config.cs b/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/Config.cs index 366a6b9..207d784 100644 --- a/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/Config.cs +++ b/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/Config.cs @@ -15,6 +15,10 @@ public static class Config { public static int NS_PORT = 9090; public static int NS_BCPORT = 9091; public static bool SERPENT_INDENT = false; + public static bool SSL = false; + public static string SSL_CACERTS = ""; + public static string SSL_CLIENTCERT = ""; // only clientcert option because it needs to be in pkcs12 format which integrates the key. PEM files are not supported! + public static string SSL_CLIENTCERTPASSWD = ""; public const int PROTOCOL_VERSION = 502; // Pyro5 public const string PYROLITE_VERSION="5.0"; diff --git a/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/PyroProxy.cs b/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/PyroProxy.cs index 5bcdba5..3f96d6f 100644 --- a/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/PyroProxy.cs +++ b/dotnet/Razorvine.Pyrolite/Pyrolite/Pyro/PyroProxy.cs @@ -8,8 +8,10 @@ using System.IO; using System.IO.Compression; using System.Linq; +using System.Net.Security; using System.Net.Sockets; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Text; using Razorvine.Pyro.Serializer; @@ -37,7 +39,7 @@ public class PyroProxy : DynamicObject, IDisposable { private ushort sequenceNr; private TcpClient sock; - private NetworkStream sock_stream; + private Stream sock_stream; public ISet pyroMethods = new HashSet(); // remote methods public ISet pyroAttrs = new HashSet(); // remote attributes @@ -83,7 +85,23 @@ protected void connect() { sock = new TcpClient(); sock.Connect(hostname,port); sock.NoDelay=true; - sock_stream=sock.GetStream(); + + if (Config.SSL) + { + var sslStream = new SslStream(sock.GetStream(), true, ValidateServerCertificate); + if (File.Exists(Config.SSL_CLIENTCERT)) + { + var clientCert = new X509CertificateCollection(); + clientCert.Add(new X509Certificate(Config.SSL_CLIENTCERT, Config.SSL_CLIENTCERTPASSWD)); + sslStream.AuthenticateAsClient(hostname, clientCert, System.Security.Authentication.SslProtocols.Default, false); + } else { + sslStream.AuthenticateAsClient(hostname); + } + sock_stream = sslStream; + } else { + sock_stream = sock.GetStream(); + } + sequenceNr = 0; _handshake(); @@ -97,6 +115,35 @@ protected void connect() { } } + /// + /// custom certificate validator to trust self signed CAs + /// + public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) { + X509Certificate2Collection trustedCaCollection = new X509Certificate2Collection(); + + if (File.Exists(Config.SSL_CACERTS)) { + X509Certificate2 authority = new X509Certificate2(Config.SSL_CACERTS); + trustedCaCollection.Add(authority); + } else if (Directory.Exists(Config.SSL_CACERTS) && Directory.GetFiles(Config.SSL_CACERTS).Length > 0) { + foreach (var file in Directory.GetFiles(Config.SSL_CACERTS)) + { + X509Certificate2 authority = new X509Certificate2(Config.SSL_CACERTS); + trustedCaCollection.Add(authority); + } + } + + if (chain.ChainStatus.Any(status => status.Status != X509ChainStatusFlags.UntrustedRoot)) + return false; + + foreach (var element in chain.ChainElements) + { + if (element.ChainElementStatus.Any(status => status.Status == X509ChainStatusFlags.UntrustedRoot) && !trustedCaCollection.Contains(element.Certificate)) + return false; + } + + return true; + } + /// /// get metadata from server (methods, attrs, oneway, ...) and remember them in some attributes of the proxy /// @@ -182,7 +229,7 @@ public override bool TrySetMember(SetMemberBinder binder, object value) return true; } - + /// /// Call a method on the remote Pyro object this proxy is for. ///