The basic issue is that PKIX (IETF working group that published the
specifications) is very complicated. Specs at
https://datatracker.ietf.org/wg/pkix/documents/
and I bet there are updates out of working group context.
This note explains what's really happening, vs what to type, and should
help you make sense of various advice, some (most?) of which is surely
confused.
The basic certificate scheme is that there are a number of certificate
authorities, which more or less means a self-signed certificate that
people intend to have sign otheer certificates. There is a social
consensus about which ones are trustworthy, more or less (there be
dragons here). In the Free Software world, this is typically encoded in
the root set published by Mozilla. Policy is at
https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/policy/
and the bits are in a hg repo. In the proprietary world, the set is
often a little bit different, but it's different entites with similar
goals and mostly it amounts to the same thing.
A root CA certificate more or less means a self-signed cert from an
entity that behaves as a CA. It's ambiguous if any particular person/sw
trusts it.
The word "trust anchor" refers to a self-signed cert, which in a
particular entity (that validates certificates) has been configured to
be an acceptable starting point for validation.
By default, operating systems come with a particular set of trust
anchors preconfigured. Some programs choose to have their own set and
configuration mechanisms instead of using the OS set.
Certificates have key usage indications and constraints. End entity
certs can't sign other certs (well, you could make a signature, but
validators will reject it). Even CA certs can have contraints; a root
CA can sign a CA cert for
foo.com which constrains that sub-CA to only
sign
bar.foo.com certs for vairous values of bar. This is complicated,
but the point is that when you create your own CA cert, it has to be
coded to be a CA.
When a client makes a TLS connection to a server and the server presents
certificates, both the server's certificate and zero or more
intermediate certificates ("chain" is often used for intermediates, with
"fullchain" referring to the server's cert plus the intermedidate certs;
Let's Encrypt names files this wey), the client does a validation
procedure, which involves:
- Does the name the user used to make the connection match the server
cert? The original X.509 certs were about people, not computers,
and people typically put domain names in subject alternative names.
In order to succeed here, the validating software must be ok with
matching the intended name with the offered cert.
This means that if you have a web server that serves multiple names,
the cert it uses must be valid for all of them. (There is also SNI
which I think can be used to ask the server to act as a particular
target.)
I have not tried to make certs with IP addresses. I would not be
sure that an "inet_ntoa(ipaddr)" CN would be matched by openssl or
browsers, but I would not assert that it won't be.
- Given the server cert and chain (call this 1..N), is it true that
both:
+ the cert that signed the Nth in the offered chain is a configured
trust anchor?
+ for all values of i from 1 to N, is cert i signed by cert i+1,
does the signature match, is the date within range, and all the
rest of the constraint checks are ok? (And no certs have been
revoked and/or OCSP validation is ok, depending.)
In firefox, you can click on the lock, more info, connection secure,
view certificate and then see the EE cert at left, chain certs in the
middle (0 or more) and the trust anchor cert on the right.
When you make your own CA and try to use it, besides putting the right
contents in all the files, the hard parts are:
Having a scheme for how the server will be named (what name will be
used to access it) and encoding that in the server's cert so that
validators will be content.
Configuring the CA cert as a trust anchor in all software that will
validate the cert.
For operating a local CA, I use easyrsa:
https://community.openvpn.net/Pages/EasyRSA (sorry, cloudflare clickthrough)
https://easy-rsa.readthedocs.io/en/latest/
https://github.com/OpenVPN/easy-rsa
I recommend not using IP addreses. Instead, assign a FQDN *even if it
is not valid*, run your own nameserver on your home network, and
configure a "response policy zone" to return the LAN address of your
server when a host asks for "
mqtt.foo.org" (for your chosen foo). Then,
put
mqtt.foo.org in the CA as the subjectAltName, so that your certs are
structurally the same as all the rest of them. That will avoid all the
issues about "Does browser X handle
https://1.2.3.4/ the way I want it
to?".
Also, you can then change the IP address, which is not part of
validation. The user/software asked to connect to
mqtt.foo.org and it
did, and what IP address it has today is not part of ensuring it got to
the right place.
Or, you can register a real domain and use let's encrypt and have a real
certificate, even if you don't want to expose the server. But you'll
have to set up to pass one of the challenges. This is what I mostly do
and it takes "confgure trust anchor in every validating software" off
the table.