ZeroMQ C# Ironhouse example
Asked Answered
E

2

9

I am fairly new to ZeroMQ and have been comparing security of messages using the ZeroMQ NuGet package and the NetMQ & NetMQ Security NuGet packages.

So far, I have not been able to find a C# version of the Ironhouse example using Curve Security. There is a "todo" item on the ZGuides repo but so far nothing implemented. (https://github.com/metadings/zguide/issues/1)

I am also trying to determine whether the NetMQ.Security approach to security is better than the curve security approach that is built into ZeroMQ 4. It seems like most information about Curve is at least from 2014 or earlier.

Any information would be greatly appreciated!

Equisetum answered 13/10, 2017 at 6:45 Comment(5)
" It seems like most information about Curve is at least from 2014 or earlier." - that might me examine whether it's still supported/active?Singapore
@BrianBehm Would you mind to rather be explicit on what are your features and what are their respective metrics, that you plan to collect and use to "determine" which "approach to security is better than" other? Without any explicit metric, none such comparison seems possible to be rigorous to be expressed, be merit-focused & be records-of-evidence based. Thanks in advance for adding the rules.Indolence
@user3666197, "better" may have been the wrong word to use. I know that ZeroMQ has implemented CurveZMQ and the clrzmq4 binding seems to have carried that on. NetMQ seems to have chosen a different approach and does not support CurveZMQ. So maybe the better question is, "Has CurveZMQ fallen out of favor and thus is the approach taken by NetMQ preferred?". Ideally, I am really looking for some working C# code that implements CurveZMQ in C# so I can make the comparisons for myself which why I added the bounty. Hopefully this answers your question.Equisetum
@BrianBehm Actually no, it does not. But this might be due to my limited imagination -- was trying to imagine another word -- how does an augmented human look like? I just try to understand easy and simple things & somehow cannot imagine how a delivery of an augmented human may actually take place [_ It indeed seems I am too old to understand everything :o) _]. BTW: also have seen your other post about ZeroMQ -- have you made a plain ( non-Curve'd ) PUB/SUB to communicate, before asking on S/O about a colliding Curve'd modus operandi?Indolence
@user3666197, I have been able to get the code working for a non-curve implementation. I believe it would have been the NULL security approach. If I remember correctly, PLAIN requires a username/password which I have not tried since it doesn't suit the real use case for my application. In my example code, if you comment out the few lines that have to do with setting options for curve keys, the communication works like a champ.Equisetum
J
4

Both publisher and subscriber need to use its own set of public\private keys. In your sample code for subscriber you set CurvePublicKey (to that of server, which is wrong but still) but do not set CurveSecretKey - that's why you get "cannot open client INITIATE vouch". Here is your sample from another question fixed:

public class Program
{
    static void Main(string[] args) {
        using (var context = new ZContext()) {
            Console.WriteLine($"Curve Supported: {ZeroMQ.ZContext.Has("curve")}");
            byte[] serverPublicKey;
            byte[] serverSecretKey;
            Z85.CurveKeypair(out serverPublicKey, out serverSecretKey);

            var publisher = new ZSocket(context, ZSocketType.PUB);
            publisher.CurvePublicKey = serverPublicKey;
            publisher.CurveSecretKey = serverSecretKey;
            publisher.CurveServer = true;
            publisher.Bind("tcp://*:5050");

            var subscriber = new ZSocket(context, ZSocketType.SUB);
            byte[] subPublicKey;
            byte[] subSecretKey;
            Z85.CurveKeypair(out subPublicKey, out subSecretKey);
            subscriber.CurvePublicKey = subPublicKey;
            subscriber.CurveSecretKey = subSecretKey;
            subscriber.CurveServerKey = serverPublicKey;
            ZError connectError;
            subscriber.Connect("tcp://mybox:5050", out connectError);
            if (connectError != null) {
                Console.WriteLine($"Connection error: {connectError.Name} - {connectError.Number} - {connectError.Text}");
            }
            subscriber.SubscribeAll();

            // Publish some messages
            Task.Run(() => {
                for (var i = 1; i <= 5; i++) {
                    var msg = $"Pub msg: {Guid.NewGuid().ToString()}";
                    using (var frame = new ZFrame(msg)) {
                        publisher.Send(frame);
                    }
                }
            });
            Task.Run(() => {
                // Receive some messages
                while (true) {
                    using (var frame = subscriber.ReceiveFrame()) {
                        var msg = frame.ReadString();
                        Console.WriteLine($"Received: {msg}");
                    }
                }
            });
            Console.WriteLine("Press ENTER to exit");
            Console.ReadLine();
            ZError subError;
            subscriber.Disconnect("tcp://mybox:5050", out subError);
            subscriber.Dispose();
            ZError pubError;
            publisher.Disconnect("tcp://*:5050", out pubError);
            publisher.Dispose();
        }
    }
}
Jorgensen answered 1/11, 2017 at 10:0 Comment(3)
I haven't verified this code yet but looks great. You are right though, the thing I was missing was the fact that the client and server have their own KeyPairs. I will award you the bounty points.Equisetum
Evk, if you'd like to post this example on my other question, I will mark yours as the answer there as well. #46945449Equisetum
@BrianBehm it's discouraged to copy paste answers so I closed that question as duplucate of this one.Jorgensen
D
1

Indeed, there are not much C# examples with NetMQ. I found this that works "CurveTests.cs":

    public void CurveTest()
    {
        var serverPair = new NetMQCertificate();
        using var server = new DealerSocket();
        server.Options.CurveServer = true;
        server.Options.CurveCertificate = serverPair;
        server.Bind($"tcp://127.0.0.1:55367");
        
        var clientPair = new NetMQCertificate();
        using var client = new DealerSocket();
        client.Options.CurveServerKey = serverPair.PublicKey;
        client.Options.CurveCertificate = clientPair;
        client.Connect("tcp://127.0.0.1:55367");

        for (int i = 0; i < 100; i++)
        {
            client.SendFrame("Hello");
            var hello = server.ReceiveFrameString();
            Assert.Equal("Hello", hello);
            
            server.SendFrame("World");
            var world = client.ReceiveFrameString();
            Assert.Equal("World", world);
        }
    }

Important note - if you want to share server public key between different applications, don't use string representation (serverPair.PublicKeyZ85), because encryption won't work. I assume it related to encoding. Better save byte array representation to some file and share it instead:

File.WriteAllBytes("serverPublicKey.txt", serverPair.PublicKey);
Durant answered 29/7, 2021 at 9:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.