Here's the complete code file that works for me. You will (obviously) need to change the names of the servers, and the thumbprint of your certificate.
Note that this code only uses your client and server's certificates to establish a secure connection. You are still logging in as guest. I will show you how to use your client certificate to authenticate yourself below.
using System;And the output:
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using RabbitMQ.Client;
using RabbitMQ.Util;
namespace RabbitSslTest
{
class Program
{
static void Main(string[] args)
{
Program p = new Program();
p.Start();
Console.WriteLine();
Console.WriteLine("Press <ENTER> to exit.");
Console.ReadLine();
}
public void Start()
{
try
{
// we use the rabbit connection factory, just like normal
ConnectionFactory cf = new ConnectionFactory();
// set the hostname and the port
cf.HostName = "rabbitmq-server";
cf.Port = AmqpTcpEndpoint.DefaultAmqpSslPort;
// I've imported my certificate into my certificate store
// (the Personal/Certificates folder in the certmgr mmc snap-in)
// Let's open that store right now.
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);
// and find my certificate by its thumbprint.
X509Certificate cert = store.Certificates
.Find(
X509FindType.FindByThumbprint,
"6d 36 c0 f9 c2 f8 72 05 d1 01 6c 54 e1 91 bb 2d 66 7b ac 94",
true
)
.OfType<X509Certificate>()
.First();
// now, let's set the connection factory's ssl-specific settings
// NOTE: it's absolutely required that what you set as Ssl.ServerName be
// what's on your rabbitmq server's certificate (its CN - common name)
cf.Ssl.Certs = new X509CertificateCollection(new X509Certificate[] { cert });
cf.Ssl.ServerName = "rabbitmq-server";
cf.Ssl.Enabled = true;
// we're ready to create an SSL connection to the rabbitmq server
using (IConnection conn = cf.CreateConnection())
{
using (IModel channel = conn.CreateModel())
{
channel.QueueDeclare("rabbitmq-dotnet-test", false, false, false, null);
// create some basic properties for the message we're going to send
IBasicProperties props = channel.CreateBasicProperties();
props.ContentEncoding = "utf-8";
props.MessageId = Guid.NewGuid().ToString();
// publish a hello world message
channel.BasicPublish("", "rabbitmq-dotnet-test", props, Encoding.UTF8.GetBytes("Hello, World"));
// and go get it
BasicGetResult result = channel.BasicGet("rabbitmq-dotnet-test", true);
if (null == result)
{
Console.WriteLine("No message received.");
}
else
{
Console.WriteLine("-----==<([ Received ])>==-----");
DebugUtil.DumpProperties(result, Console.Out, 0);
}
channel.QueueDelete("rabbitmq-dotnet-test");
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}

Using your certificate to authenticate
If you want to authenticate yourself with your certificate as well, you need to change the code and your rabbitmq server's configuration.
First, update your rabbitmq server's configuration. If you don't know where to look for that, read this page. Here's what mine looks like:
[The thing to note there is the addition of {auth_mechanisms,['EXTERNAL']}. Don't forget to stop your service, re-install it, and start it after making these changes.
{rabbit, [
{auth_mechanisms,['EXTERNAL']},
{ssl_listeners, [5671]},
{ssl_options, [{cacertfile,"C:/Path/To/Your/cacert.pem"},
{certfile,"C:/Path/To/Your/cert.pem"},
{keyfile,"C:/Path/To/Your/key.pem"},
{verify,verify_peer},
{fail_if_no_peer_cert,true}]}
]}
].
Next, let's update our client code just a tad by setting the connection factory's auth mechanisms.rabbitmq-service.bat stop rabbitmq-service.bat install rabbitmq-service.bat start
// we use the rabbit connection factory, just like normal ConnectionFactory cf = new ConnectionFactory();The other change I made was to insert a Console.ReadLine() just before all of my using () statements cause everything to close down. By doing this, I can view the RabbitMQ management web app to ensure that I'm authenticating using my certificate.
// set the hostname and the port
cf.HostName = "rabbitmq-server";
cf.Port = AmqpTcpEndpoint.DefaultAmqpSslPort;
cf.AuthMechanisms = new AuthMechanismFactory[] { new ExternalMechanismFactory() };
// I've imported my certificate into my certificate store
// (the Personal/Certificates folder in the certmgr mmc snap-in)
// Let's open that store right now.
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.ReadOnly);

Great article John. The only other thing I had to do was execute rabbitmq-plugins enable rabbitmq_auth_mechanism_ssl and restart the server. Otherwise, client's would fail to authenticate because rabbitmq wasn't providing the ssl option as an authentication mechanism.
ReplyDelete