Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Subscribe with certificates #86

Closed
ochrin opened this issue Jun 30, 2020 · 23 comments
Closed

Subscribe with certificates #86

ochrin opened this issue Jun 30, 2020 · 23 comments

Comments

@ochrin
Copy link

ochrin commented Jun 30, 2020

I am trying to subscribe to a topic with certificates but I am getting this error:
Err(Network(Io(Custom { kind: InvalidData, error: WebPKIError(BadDER) })))
I am not sure I am configuring correctly the MqttOptions. Here is what I did:

let ca: Vec<u8> = fs::read("/etc/mosquitto/certs/mqtt-ca.crt").expect("Something went wrong reading certificate!");
let mut mqttoptions = MqttOptions::new("air", "mysite.com", 8884);
mqttoptions.set_ca(ca);
mqttoptions.set_credentials("my_username", "my_password");
mqttoptions
	.set_keep_alive(5)
	.set_throttle(Duration::from_secs(2));

let (mut client, mut connection) = Client::new(mqttoptions, 10);
client.subscribe("read/+/meas", QoS::ExactlyOnce).unwrap();

// Iterate to poll the eventloop for connection progress
for (i, notification) in connection.iter().enumerate() {
	println!("Notification {} = {:?}", i, notification);
}

For reference, the mosquitto command I am using and which is working.
mosquitto_sub --cafile /etc/mosquitto/certs/mqtt-ca.crt -h mysite.com -p 8884 -u 'my_username' -P 'my_password' -t 'read/+/error' -v

As I am also beginning with Rust, I am not sure the ca should be provided as I did.

Any idea what I could do wrong?

@amrx101
Copy link
Contributor

amrx101 commented Jun 30, 2020

@ochrin I can give some pointers to it as I have seen this.

We use Rustls for TLS handshakes. RustTls is a modern library which is not as forgiving as OpenSSL.

Now the error originates from Rustls during the verification of the your server certificate. Most probably the server certificate is not a version3 x509 certificate. If you have access to server certificate just do:

openssl x509 -in server.crt --text --noout to check the version.

If you dont have access to it, check the version of ca.crt. If ca.crt is version 1 then the server cert will also be of the same version.

openssl x509 -in mqtt-ca.crt --text --noout.

If the certificates were generated without v3 extensions, then they will be of version 1. If the systems have not already been deployed then dont do the mistake of commissioning version 1 certificate(their spec came out in 1988), use the cert generator script in the utils directory to generate nice v3 certs.

Always good to keep handy.

PS: This library won't work with v1 or v2 x509 certs.

EDIT: rustls/rustls#127

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

Thanks for your reply.
As far as I can see I am on version 3
openssl x509 -in mqtt-ca.crt --text --noout gives:

Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
        ...
        X509v3 extensions:
            X509v3 Subject Key Identifier: 
                2D:39:88:37:D4:E3:10:2D:B0:23:A9:C9:28:6D:EA:D5:24:C6:EB:CB
            X509v3 Authority Key Identifier: 
                keyid:2D:39:88:37:D4:E3:10:2D:B0:23:A9:C9:28:6D:EA:D5:24:C6:EB:CB

            X509v3 Basic Constraints: critical
                CA:TRUE

So either RustTls does not like something else in my certificate or the problem is coming from something else...

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

Does your certificate have SAN set?

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , Check for SAN in your server certificate.. Many v3 certs have SAN missing and rejected by Rustls. If that is the case, please use this
https://github.com/paritytech/x509-signature. (Requires Rust >= 1.42). The linked project has been explicitly developed because Rustls rejects v3 certs without SAN.

X509v3 extensions:
        X509v3 Subject Alternative Name: 
            DNS:crabs.crabs, DNS:localhost

Here is the SAN under extensions in one of my local cert that I use frequently with Rustls.

EDIT: More context on this briansmith/webpki#90

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

Yes, looks like I don't have a SAN.
Now, I need to understand what it means as I am not too familiar with all these certificates.
But I don't see why I would need a SAN as I have only a single ip address. Need to dig in :(

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

Do I understand correctly that if I want to use certificates without SAN I would need to use x509-signature instead of rusttls in your rumqttc code? And I mean here code rework not just config parameters changes.

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

@ochrin You can just think of SAN as the next version of common name (CN)

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , As I understand:

  • Underlying Rustls library does not support certs without SAN in them.
  • The easiest way out would be to generate a new server but this time with SAN(if this is feasible).
  • Yup SAN should not be required when there is no shared hosting. In fact in most non web uses SAN should not be required, but Rustls is opinionated.
  • If 2 is not visible, you will have to do some work as stated the issue I linked(webpki). Rustls provides a hook for custom verifier.You would have to enable that and then use that one. The other library I linked will be used in doing all things like verify, parse etc.

Without SAN , I dont believe we can use this lib as it is. Maybe @tekjar can shed some light on it.

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

@ochrin Yes. Certificate with SAN is a requirement (SAN is compulsory in the TLS spec and rustls just follows it).

Can you let me know what the problem is with generating new certificates? Is this a hosted broker from a third-party vendor? If it helps, we have the below tool to generate certificates easily. The CommonName field there also fills SAN (as the spec says)

https://github.com/bytebeamio/provision/blob/master/provision.go#L106

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

Ok, thanks for all your replies.
Generating a new certificate may request a lot of work (not on server but on devices) which was planned for later.
Hooking rustls is beyond my competencies for now.
So for now, I think I will try to find a rust mqtt client which don't use rustls...

@ochrin ochrin closed this as completed Jul 1, 2020
@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

@tekjar .
Generating a new certificate is not a problem.
But my certificate is also hard coded in all my devices. So this means I need to update all devices.
But if I update server first, the devices can't connect anymore to mqtt broker and can't reply to update request.
And if I update devices first, then they can't connect to mqtt broker anymore unless someone reset them after server has been updated. Maybe this could work w/o reset, but I would need to test this before.
So in the end, I would either need to rework my firmware so devices can handle such a case but this was planned for later now or do more tests. It's not a thing I will do in one day...

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

@ochrin Why can't you use same CA and just reprovision server cert (with SAN) and restart the broker? That way your existing devices will continue to work?

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

I did a quick search this morning and thought it was not possible without reissuing the CA. But from what you say I may have misunderstood.
Let me check if I can do it...

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , I am afraid you are mistaken. We dont need to reissue the CA to generate new cert. Just generate new cert with SAN and provision that on your device and it will connect.

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

Would you have a link on how to do it? I can't figure it out.

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

https://jamielinux.com/docs/openssl-certificate-authority/sign-server-and-client-certificates.html

Its pretty simple. Just make sure that you have a san entry in req category in your openssl.cnf. Or look up the utils directory in this project itself.

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

@ochrin @amrx101 I used to have scripts based on openssl but they became hard to maintain and modify. I've this go tool now

  • clone this repo
  • go build provision.go (you need to have go compiler)
  • place ca.cert.pem and ca.key.pem in the current dir (names are hardcoded and hence should match)
  • ./provision -server {your domain}

Update: Ok I've noticed it's private. I'll probably make it public in some time

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , If you currently not use RSA certs you could use this https://crates.io/crates/rcgen. I use this every now and then, but I have completely gotten rid of RSA certs in my personal ecosystem of certificates. I had a Go project that I needed my ex-employer open source, they never did.

@ochrin
Copy link
Author

ochrin commented Jul 1, 2020

In the end, I think I succeeded to generate 'certificates' with SAN. See below
I restarted my mosquitto broker and my devices can still connect. And I also can connect using mosquitto_sub.
From rust point of view, the previous error is gone but a new one is there :(

ERROR rumqttc::state > Connection failed. Connection error = ServerUnavailable
Notification 0 = Err(MqttState(Connect(ServerUnavailable)))

Note that may help someone:
I am not familiar at all with certificates and was following this tuto to generate them:
https://asciinema.org/a/201826
I just changed the last command
openssl x509 -req -in server.csr -CA my-ca.crt -CAkey my-ca.key -CAcreateserial -out server.crt -days 180
and replaced it with
openssl x509 -req -in server.csr -CA my-ca.crt -CAkey my-ca.key -CAcreateserial -out server.crt -days 180 -extfile cert.conf
where cert.conf is

authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = your.servername.com
# DNS.2 = another.servername.com
# DNS.3 = another dev domain

# IP.1 = An ip address
# IP.2 = Another ip address 

It generates a server.crt file with SAN...

@ochrin ochrin reopened this Jul 1, 2020
@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , that error is completely independent of certs. That error is indicative of server not being available at end point pointed by client.

@tekjar
Copy link
Contributor

tekjar commented Jul 1, 2020

@ochrin Your TLS setup worked and the connection is successful. But the broker is disconnecting it immediately because of some other reason. You need to check broker logs

ServerUnavailable is a bug in the current version (next version will fix this). Actual error should have been NotAuthorized)

@amrx101
Copy link
Contributor

amrx101 commented Jul 1, 2020

@ochrin , just confirm and double confirm that the server certificate CN and SAN has the exact domain name of your server. So if you connect to mysite.com the CN and SAN should have mysite.com.

Scratch that. As @tekjar said that TLS handshake was negotiated, no need to check this.

@ochrin
Copy link
Author

ochrin commented Jul 2, 2020

My mistake: a bad copy paste for the password. So now it is working.
Thanks for your help.
I have one more question, but I will open a new thread.

@ochrin ochrin closed this as completed Jul 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants