diff options
5 files changed, 137 insertions, 53 deletions
diff --git a/dotnet/client-010/client/client/Client.cs b/dotnet/client-010/client/client/Client.cs index 864cf65186..fc9ff22191 100644 --- a/dotnet/client-010/client/client/Client.cs +++ b/dotnet/client-010/client/client/Client.cs @@ -22,6 +22,7 @@ using System.Threading; using org.apache.qpid.transport; using org.apache.qpid.transport.network.io; using org.apache.qpid.transport.util; +using System.Security.Cryptography.X509Certificates; namespace org.apache.qpid.client { @@ -35,6 +36,7 @@ namespace org.apache.qpid.client private IClosedListener _closedListner; public event EventHandler<ExceptionArgs> ExceptionRaised; + public event EventHandler ConnectionOpenOK; public event EventHandler ConnectionLost; public bool IsClosed @@ -61,6 +63,11 @@ namespace org.apache.qpid.client #region Interface IClient + public void Connect(String host, int port, String virtualHost, String username, String password) + { + Connect(host, port, virtualHost, username, password, "PLAIN"); + } + /// <summary> /// Establishes a connection with a broker using the provided user auths /// @@ -70,11 +77,12 @@ namespace org.apache.qpid.client /// <param name="virtualHost">virtual host name</param> /// <param name="username">User Name</param> /// <param name="password">Password</param> - public void Connect(String host, int port, String virtualHost, String username, String password) + /// <param name="mechanism">SASL authentication mechanism, possible values: PLAIN, EXTERNAL, DIGEST-MD5, ANONYMOUS</param> + public void Connect(String host, int port, String virtualHost, String username, String password, String mechanism) { - _log.Debug(String.Format("Client Connecting to host {0}; port {1}; virtualHost {2}; username {3}", host, - port, virtualHost, username)); - ClientConnectionDelegate connectionDelegate = new ClientConnectionDelegate(this, username, password); + _log.Debug(String.Format("Client Connecting to host {0}; port {1}; virtualHost {2}; username {3}; mechanism {4}", + host, port, virtualHost, username, mechanism)); + ClientConnectionDelegate connectionDelegate = new ClientConnectionDelegate(this, username, password, mechanism); ManualResetEvent negotiationComplete = new ManualResetEvent(false); connectionDelegate.SetCondition(negotiationComplete); connectionDelegate.VirtualHost = virtualHost; @@ -99,27 +107,36 @@ namespace org.apache.qpid.client /// <param name="virtualHost">virtual host name</param> /// <param name="username">User Name</param> /// <param name="password">Password</param> + /// <param name="mechanism">SASL authentication mechanism, possible values: PLAIN, EXTERNAL, DIGEST-MD5, ANONYMOUS</param> /// <param name="serverName">Name of the SSL server</param> /// <param name="certPath">Path to the X509 certificate to be used for client authentication</param> + /// <param name="certPass">Password to certificate file, pass null if no password is required</param> /// <param name="rejectUntrusted">If true connection will not be established if the broker is not trusted</param> - public void ConnectSSL(String host, int port, String virtualHost, String username, String password, string serverName, string certPath, bool rejectUntrusted) + public void ConnectSSL(String host, int port, String virtualHost, String username, String password, String mechanism, string serverName, string certPath, String certPass, bool rejectUntrusted) { - _log.Debug(String.Format("Client Connecting to host {0}; port {1}; virtualHost {2}; username {3}", host, - port, virtualHost, username)); - _log.Debug(String.Format("SSL paramters: serverName: {0}; certPath: {1}; rejectUntrusted: {2}", serverName, certPath, rejectUntrusted)); - ClientConnectionDelegate connectionDelegate = new ClientConnectionDelegate(this, username, password); - ManualResetEvent negotiationComplete = new ManualResetEvent(false); - connectionDelegate.SetCondition(negotiationComplete); - connectionDelegate.VirtualHost = virtualHost; - _conn = IoSSLTransport.Connect(host, port, serverName, certPath, rejectUntrusted, connectionDelegate); - - _conn.Send(new ProtocolHeader(1, 0, 10)); - negotiationComplete.WaitOne(); + _log.Debug(String.Format("Client Connecting to host {0}; port {1}; virtualHost {2}; username {3}; mechanism {4}", + host, port, virtualHost, username, mechanism)); + _log.Debug(String.Format("SSL parameters: serverName: {0}; certPath: {1}; rejectUntrusted: {2}", serverName, certPath, rejectUntrusted)); + _conn = IoSSLTransport.Connect(host, port, virtualHost, mechanism, serverName, certPath, certPass, rejectUntrusted, this); + } - if (connectionDelegate.Exception != null) - throw connectionDelegate.Exception; + /// <summary> + /// Establishes a connection with a broker using SSL + /// + /// </summary> + /// <param name="host">Host name on which a broker is deployed</param> + /// <param name="port">Broker port </param> + /// <param name="mechanism">SASL authentication mechanism, possible values: PLAIN, EXTERNAL, DIGEST-MD5, ANONYMOUS</param> + /// <param name="certificate">X509 certificate to be used for client authentication</param> + /// <param name="rejectUntrusted">If true connection will not be established if the broker is not trusted</param> + public void ConnectSSL(String host, int port, String mechanism, X509Certificate certificate, bool rejectUntrusted) + { + _log.Debug(String.Format("Client Connecting to host {0}; port {1}; mechanism {2}", + host, port, mechanism)); + _log.Debug(String.Format("SSL parameters: certSubject: {0}; rejectUntrusted: {1}", + certificate.Subject, rejectUntrusted)); - connectionDelegate.SetCondition(null); + _conn = IoSSLTransport.Connect(host, port, mechanism, certificate, rejectUntrusted, this); } public void Close() @@ -166,5 +183,13 @@ namespace org.apache.qpid.client if (ExceptionRaised != null) ExceptionRaised(this, new ExceptionArgs(exception)); } + + internal void ConnectionOpenOk(Channel context, ConnectionOpenOk mstruct) + { + if (ConnectionOpenOK != null) + { + ConnectionOpenOK(this, new EventArgs()); + } + } } } diff --git a/dotnet/client-010/client/client/ClientConnectionDelegate.cs b/dotnet/client-010/client/client/ClientConnectionDelegate.cs index 9999f5312d..83aac80e83 100644 --- a/dotnet/client-010/client/client/ClientConnectionDelegate.cs +++ b/dotnet/client-010/client/client/ClientConnectionDelegate.cs @@ -33,6 +33,9 @@ namespace org.apache.qpid.client private readonly Client _client; private string _username; private string _password; + + // PLAIN SASL mechanism by default + private string _mechanism = "PLAIN"; private Exception _exception; public ClientConnectionDelegate(Client client, string username, string pasword) @@ -42,6 +45,12 @@ namespace org.apache.qpid.client _password = pasword; } + public ClientConnectionDelegate(Client client, string username, string pasword, string mechanism) + : this(client, username, pasword) + { + _mechanism = mechanism; + } + public Exception Exception { get { return _exception; } @@ -64,16 +73,27 @@ namespace org.apache.qpid.client public override void ConnectionStart(Channel context, ConnectionStart mystruct) { - const string mechanism = "PLAIN"; MemoryStream stResponse = new MemoryStream(); - byte[] part = Encoding.UTF8.GetBytes(_username); - stResponse.WriteByte(0); - stResponse.Write(part, 0, part.Length); - stResponse.WriteByte(0); - part = Encoding.UTF8.GetBytes(_password); - stResponse.Write(part, 0, part.Length); + + // do not send username and password for EXTERNAL mechanism, + // because they are inside a certificate file + if (_mechanism != "EXTERNAL") + { + byte[] part = Encoding.UTF8.GetBytes(_username); + stResponse.WriteByte(0); + stResponse.Write(part, 0, part.Length); + stResponse.WriteByte(0); + part = Encoding.UTF8.GetBytes(_password); + stResponse.Write(part, 0, part.Length); + } Dictionary<String, Object> props = new Dictionary<String, Object>(); - context.ConnectionStartOk(props, mechanism, stResponse.ToArray(), "utf8"); + context.ConnectionStartOk(props, _mechanism, stResponse.ToArray(), "utf8"); + } + + public override void ConnectionOpenOk(Channel context, ConnectionOpenOk mstruct) + { + base.ConnectionOpenOk(context, mstruct); + _client.ConnectionOpenOk(context, mstruct); } public override void Closed() diff --git a/dotnet/client-010/client/client/IClient.cs b/dotnet/client-010/client/client/IClient.cs index 6fe4a4d4b8..b7b6c26957 100644 --- a/dotnet/client-010/client/client/IClient.cs +++ b/dotnet/client-010/client/client/IClient.cs @@ -33,7 +33,8 @@ namespace org.apache.qpid.client /// <param name="virtualHost">virtualHost the virtual host name</param> /// <param name="username"> username user name</param> /// <param name="passwor">password password</param> - void Connect(String host, int port, String virtualHost, String username, String passwor); + void Connect(String host, int port, String virtualHost, String username, String password, String mechanism); + void Connect(String host, int port, String virtualHost, String username, String password); /// <summary> /// Close this client @@ -76,6 +77,6 @@ namespace org.apache.qpid.client /// <param name="serverName">Name of the SSL server</param> /// <param name="certPath">Path to the X509 certificate to be used for client authentication</param> /// <param name="rejectUntrusted">If true connection will not be established if the broker is not trusted</param> - void ConnectSSL(String host, int port, String virtualHost, String username, String password, string serverName, string certPath, bool rejectUntrusted); + void ConnectSSL(String host, int port, String virtualHost, String username, String password, String mechanism, string serverName, string certPath, String certPass, bool rejectUntrusted); } } diff --git a/dotnet/client-010/client/transport/network/io/IoSSLTransport.cs b/dotnet/client-010/client/transport/network/io/IoSSLTransport.cs index 8b36693bc2..b6c7940a1d 100644 --- a/dotnet/client-010/client/transport/network/io/IoSSLTransport.cs +++ b/dotnet/client-010/client/transport/network/io/IoSSLTransport.cs @@ -22,7 +22,10 @@ using System.Net.Security; using System.Net.Sockets; using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using System.Threading; + using org.apache.qpid.transport.util; +using org.apache.qpid.client; namespace org.apache.qpid.transport.network.io { @@ -41,16 +44,48 @@ namespace org.apache.qpid.transport.network.io private Connection m_con; private readonly bool _rejectUntrusted; - public static Connection Connect(String host, int port, string serverName, string certPath, bool rejectUntrusted, ConnectionDelegate conndel) - { - IIoTransport transport = new IoSSLTransport(host, port, serverName, certPath, rejectUntrusted, conndel); - return transport.Connection; + public static Connection Connect(String host, int port, String mechanism, X509Certificate certificate, bool rejectUntrusted, Client client) + { + ClientConnectionDelegate connectionDelegate = new ClientConnectionDelegate(client, string.Empty, string.Empty, mechanism); + ManualResetEvent negotiationComplete = new ManualResetEvent(true); + connectionDelegate.SetCondition(negotiationComplete); + connectionDelegate.VirtualHost = string.Empty; + + IIoTransport transport = new IoSSLTransport(host, port, certificate, rejectUntrusted, connectionDelegate); + + Connection _conn = transport.Connection; + _conn.Send(new ProtocolHeader(1, 0, 10)); + negotiationComplete.WaitOne(); + + if (connectionDelegate.Exception != null) + throw connectionDelegate.Exception; + + connectionDelegate.SetCondition(null); + + return _conn; + } + + public static Connection Connect(String host, int port, String virtualHost, String mechanism, string serverName, string certPath, String certPass, bool rejectUntrusted, Client client) + { + // create certificate object based on whether or not password is null + X509Certificate cert; + if (certPass != null) + { + cert = new X509Certificate2(certPath, certPass); + } + else + { + cert = X509Certificate.CreateFromCertFile(certPath); + } + + return Connect(host, port, mechanism, cert, rejectUntrusted, client); } - public IoSSLTransport(String host, int port, string serverName, string certPath, bool rejectUntrusted, ConnectionDelegate conndel) + public IoSSLTransport(String host, int port, X509Certificate certificate, bool rejectUntrusted, ConnectionDelegate conndel) { _rejectUntrusted = rejectUntrusted; - CreateSocket(host, port, serverName, certPath); + CreateSocket(host, port); + CreateSSLStream(host, Socket, certificate); Sender = new IoSender(this, QUEUE_SIZE, TIMEOUT); Receiver = new IoReceiver(Stream, Socket.ReceiveBufferSize*2, TIMEOUT); Assembler assembler = new Assembler(); @@ -101,7 +136,7 @@ namespace org.apache.qpid.transport.network.io #region Private Support Functions - private void CreateSocket(String host, int port, string serverName, string certPath) + private void CreateSocket(String host, int port) { TcpClient socket; try @@ -128,23 +163,21 @@ namespace org.apache.qpid.transport.network.io } catch (Exception e) { - throw new TransportException("Error connecting to broker", e); + throw new TransportException(string.Format("Error connecting to broker: {0}", e.Message)); } + } + + private void CreateSSLStream(String host, TcpClient socket, X509Certificate certificate) + { try { //Initializes a new instance of the SslStream class using the specified Stream, stream closure behavior, certificate validation delegate and certificate selection delegate - SslStream sslStream = new SslStream(socket.GetStream(), false, ValidateServerCertificate, LocalCertificateSelection); - if (certPath != null) - { - X509CertificateCollection col = new X509CertificateCollection(); - X509Certificate cert = X509Certificate.CreateFromCertFile(certPath); - col.Add(cert); - sslStream.AuthenticateAsClient(serverName, col, SslProtocols.Default, true); - } - else - { - sslStream.AuthenticateAsClient(serverName); - } + SslStream sslStream = new SslStream(socket.GetStream(), false, ValidateServerCertificate, LocalCertificateSelection); + + X509CertificateCollection certCol = new X509CertificateCollection(); + certCol.Add(certificate); + + sslStream.AuthenticateAsClient(host, certCol, SslProtocols.Default, true); Stream = sslStream; } catch (AuthenticationException e) @@ -153,9 +186,10 @@ namespace org.apache.qpid.transport.network.io if (e.InnerException != null) { log.Warn("Inner exception: {0}", e.InnerException.Message); + e = new AuthenticationException(e.InnerException.Message, e.InnerException); } socket.Close(); - throw new TransportException("Authentication failed - closing the connection."); + throw new TransportException(string.Format("Authentication failed, closing connection to broker: {0}", e.Message)); } } @@ -184,7 +218,8 @@ namespace org.apache.qpid.transport.network.io string[] acceptableIssuers ) { - return remoteCertificate; + // used to be return null; in the original version + return localCertificates[0]; } #endregion diff --git a/dotnet/client-010/client/transport/network/io/IoSender.cs b/dotnet/client-010/client/transport/network/io/IoSender.cs index 4491d2e98b..025b782a12 100644 --- a/dotnet/client-010/client/transport/network/io/IoSender.cs +++ b/dotnet/client-010/client/transport/network/io/IoSender.cs @@ -27,6 +27,7 @@ namespace org.apache.qpid.transport.network.io public sealed class IoSender : IIoSender<MemoryStream> { private static readonly Logger log = Logger.Get(typeof (IoReceiver)); + private readonly IIoTransport ioTransport; private readonly Stream bufStream; private bool closed; private readonly Mutex mutClosed = new Mutex(); @@ -37,6 +38,7 @@ namespace org.apache.qpid.transport.network.io public IoSender(IIoTransport transport, int queueSize, int timeout) { this.timeout = timeout; + ioTransport = transport; bufStream = transport.Stream; queue = new CircularBuffer<byte[]>(queueSize); thread = new Thread(Go); @@ -71,7 +73,7 @@ namespace org.apache.qpid.transport.network.io int length = (int)_tobeSent.Position; byte[] buf = new byte[length]; _tobeSent.Seek(0, SeekOrigin.Begin); - _tobeSent.Read(buf, 0, length); + _tobeSent.Read(buf, 0, length); queue.Enqueue(buf); // bufStream.Write(buf, 0, length); // _tobeSent = new MemoryStream(); @@ -125,7 +127,8 @@ namespace org.apache.qpid.transport.network.io } catch (Exception e) { - Console.WriteLine(e); + closed = true; + ioTransport.Connection.On_ReceivedException(this, new ExceptionArgs(e)); } } } |