mTLS ( mutual TLS) details - ssl

I know that for MTLS , both parties , Client and server exchange certificates. These certificates should be signed by a CA that both parties can trust in order to validate the certificate.
My question is , does MTLS also means in addition to verifying the certificate (if CA is trusted, leaf certificate is trusted) , either side (Server or client) can also do some additional checks like Hostname check or Whether the client connecting to the server is in the list of approved trusted entities?
Can anyone point me to the mTLS specification and what are the overheads in mTLS?

Besides what EJP has said about "MTLS" term, the TLS 1.2 specification doesn't have strict requirements regarding what information is to be checked and in which way.
It's up to the receiving party to decide if the presented certificate is to be trusted or not. This means that for example it's ok for the server to accept only certificates issued by the CA that belongs to the company that owns the server. This is how client-bank access systems often work - they accept only certificates issued by the bank and the common name of such certificate must correspond to the username provided in a web form.
Both parties are free to check any information in the certificate including direct comparison of public key hash (thus only particular keypairs will work no matter what is contained in other certificate properties).

The most up todate RFC regarding this subject is:
https://datatracker.ietf.org/doc/draft-ietf-oauth-mtls/
this is an extension to OAuth 2.0
The purpose of this document is to define a mechanism how TLS certificate is used in a context of replacing Client ID and Secret (aka, Client Credentials)
The standard establishes two mechanisms how a TLS Certificate is used as a client credential, and the associated token flows, and attributes.
The general summary of this is:
(a) Authorization Server: checks the certificate either according to PKI (signed by a valid root) The RFC does not define the options, but they are pretty self-explanatory and depend on the use case. But it can be (1) certificate is signed by a trusted root and, is not revoked, (2) individually recognize each certificate based on some logic.
(b) Resource server checks the token and client certificate (client credential, or CC), and used in the underlying TLS session. Please note that there are no validation checks concerning the certificate or its origin at TLS layer, all checks are performed at an Application layer. The resource server should, therefore, configure the TLS stack in a way that it does not verify whether the certificate presented by the client during the handshake is signed by a trusted CA certificate.
This mechanism becomes particularly interesting in some GDPR context, as it makes it impossible to share tokens between client and the server.
Overall a great feature for privacy, and improved security.

mTLS can be implemented by issuing all parties a CA certificate and adding these to all communicating parties, which is a sort of an access control list. Whoever has their CA certificate in your app's trust store is able to connect.
The trust system however is the same as with TLS in the case of a https connection - you can issue several certificates from the same CA and add the root CA certificate to the trust store of your app. It will trust all certificates issued from the same root. This is arguably easier to set up as you only have to add the app's own certificate and the CA root. However, if you want to revoke a certificate, it becomes a little more complicated.
I wrote a guide to generate certificates here:
https://thoughts-about-code.blogspot.com/2021/12/creating-a-ca-for-mtls-with-openssl.html

First:
Both server and client certificates have to be built with the same CA certificate and key
see : https://github.com/ysimonx/mtls-ssl-generator/blob/main/generate_certificates.sh
So you have to store the CA certificate on both sides :
On the server side : you can allow (this is optional) a client that provides client cert built with the same CA than its own certificate. (see rejectUnauthorized: true)
https: {
requestCert: true,
rejectUnauthorized: true,
key: fs.readFileSync("./certificates/server/serverKey.pem"),
cert: fs.readFileSync("./certificates/server/serverCrt.pem") ,
ca: [fs.readFileSync("./certificates/ca/caCrt.pem")]
}
(exemple for a fastify nodejs https self-signed certificate server)
On the client side : you can allow (this is optional too) a server that provides (or not) the same CA certificate as the client knows
curl -k --cert ./certificates/client/clientCrt.pem --key ./certificates/client/clientKey.pem https://localhost:3000/
(parameter -k : do not verify the server certificate based upon a CA authority)
or
curl --cacert ./certificates/ca/caCrt.pem --cert ./certificates/client/clientCrt.pem --key ./certificates/client/clientKey.pem https://localhost:3000/
if you want the client to match the correct CA cert used by the server

There is no 'MTLS Specification', for the very good reason that there is no such thing as 'MTLS'. You just made it up. The TLS specification, including mutual authentication, is to be found in RFC 2246 as amended.
The TLS APIs should make the peer certificate chain available to the application, so it can do any additional checking it likes.
'MTLS', insofar as it exists at all, refers to an Internet Draft for multiplexed TLS.

Related

Mutual Authentication TLS

What is the use of mutual authentication in TLS without restricting the client cert?
Here is my understanding about client/mutual auth using TLS.
The idea is that both the server the client authenticate/verifies each other certs so,
1- The client verifies the server cert based on its CA trust store
2- The server verifies the client cert based on its *CA trust store*
Now the key point to me is the second one, the server has to trust the client certificate by either saving it in the server trust store, or save the CA/ICA of the client cert, which should be private to the client, not via public CA, private CA to that client that the server wishes to trust.
Now [rfc5246][1] says the following
If the client has sent a certificate with signing ability, a digitally-signed
CertificateVerify message is sent to explicitly verify possession of
the private key in the certificate.
This won't achieve any authentication correct? for example, if I have a server that trusts any certificate signed by the trusted CAs around the world, then why bother at all with client authentication at all?
[1]: https://www.rfc-editor.org/rfc/rfc5246
When the server gets the client cert (as part of the TLS protocol), the server does all the normal cert checks, including chaining up to a trusted root. For the server to trust a client cert issued by Foo CA, the server needs to have the Foo CA root already installed at the server.
The corner stone of X.509 certs is root CA certs. A host should only trust certs issued by the CAs it trusts. So when an admin installs FooCA's roots, the admin is stating "I trust the certs issued by Foo and I trust that Foo did appropriate checks that the cert is valid and assigned to the correct entity."
The server does not need to store the client's cert, there's no reason to. When the cert comes in as part of TLS, the server simply checks it. No persistence needed, and if anything fails, and that includes not having the Foo CA root cert installed, then the connection fails.
The server DOES authenticate the client. A certificate binds a public key (in the cert) to an entity; for example CN=frodo#theshire.com. When the client connects and the server asks for the client cert, the server will check that the name in the cert (frodo#theshire.com) matches the name of the user, but it will also get the client to encrypt some data using the private key associated with the public key in the cert, and if the server successfully decrypts the data, then the client (frodo#theshire.com) really is who they claim to be. Or, in the worst case, an imposter who got access to Frodo's private key :)
Does that help?

Restrict TLS mutual authentication to specific TLS certificates

There is a PKI with a single CA issuing all the x509 certificates in my network.
So on my network all the servers and clients possess a certificate from my CA stored in their corresponding keystore together with the private key. Each server and client has also the CA certificate in a chain file available to validate the trust chain of the peer x509 certificate when TLS mutual authentication is going on. All fine.
Let’s suppose I have now on my network two servers and two clients and I want to make sure Client_A and Server_A succeed with TLS mutual authentication using their x509 certificates, as Client_B and Server_B should do.
At the same time, I want to make sure TLS mutual authentication between Client_A and Server_B will not succeed. (Also valid between Client_B and Server_A).
How can I make the servers and clients in my network not only verifying the trust chain, but also respecting some kind of whitelist?
Maybe this is not feasible on TLS Layer, that is what I want to have clarified.
You can achieve this using any of the below 2 approaches-
Verifying client certificate at TLS layer: Create separate intermediate issuing CA for each client group. For example, for all the clients of Server_A, create a CA named Ca_Client_A. So your chain will look like rootCA -> Ca_Client_A -> client certificates. Import this Ca_Client_A in the trust-store of Server-A. Now Server_A will allow connections to the clients which has issuing ca Ca_Client_A. Similarly, you can create Ca_Client_B intermediate CA for serving client group B by server B.
Verifying client certificate at Application layer: For this, you need to write authentication logic in your application server where you need to put constraints such as allowed client certificate-serial numbers or CN name matching with keyword for successful authentication.
Hope that helps.
There isn't any way (that I know of) to do this at the TLS layer.
Most TLS libraries offer a callback option during the certificate exchange, and that would be the appropriate place to check the certificate against a list. Returning the library-specific version of failure/unacceptable will usually cause a TLS handshake failure, and no data will have been transmitted.
A lot of this depends on the server software you are using, not the TLS standard itself. Some software packages can be configured to trust certificates issued by a particular CA certificate. In your scenario, this would allow ANY certificate issued by your CA to connect to ANY server configured to trust your CA certificate. Other server software can be configured to trust a particular certificate, or certificates with a given subject (distinguished name of the certificate, subjectAltName, etc.) So, it depends on which particular "server" you're trying to connect to. Is this an Apache web server? IIS? Tomcat? The answer varies depending on what server platform we're talking about.

Should the trusted Root CA be a part of the certificate chain?

I'm setting up 2-way SSL communication between services on different hosts. Let's say I have my own CA called A. A is trusted by all of my services through a centralized jks. Now let's say I have certificate B signed by A. When services send the certificate should they be sending the entire chain B - A, or just B? I believe both tend to work with most implementations.
I tried to find canonical information about this online, but I'm coming up with nothing.
Thanks for the help
As per tls - Validating an SSL certificate chain according to RFC 5280: Am I understanding this correctly? - Information Security Stack Exchange:
the server should send the exact chain that is to be used; the server
is explicitly allowed to omit the root CA, but that's all.
Reference (RFC 5246 - TLS v1.2, sec. 7.4.2. - Server Certificate):
certificate_list
This is a sequence (chain) of certificates. The sender's
certificate MUST come first in the list. Each following certificate
MUST directly certify the one preceding it. Because certificate
validation requires that root keys be distributed independently, the
self-signed certificate that specifies the root certificate
authority MAY be omitted from the chain, under the assumption that
the remote end must already possess it in order to validate it in
any case.
The idea behind the trusted root CA is that it is trusted. Would you expect the browser to trust anything sent by the server just because it includes a root CA? No!
Therefore the root CA must be already at the client and must be trusted there. It should not be included in the certificate chain by the server, but if you do it anyway browsers will simply ignore it.

WCF trust client certificates only from one root ca

When securing WCF services with server and client certificates there is an easy way to ensure the identity of the service provider certificate (server certificate), by comparing the public key or finger print of the sender certificate with an expected value.
On http://msdn.microsoft.com/en-us/library/ff648360.aspx this is explained in step 8. You can config your app.config file in the endpoint section with an identity element where you can pass in the expected encoded finger print value of the server certificate. A comparison between received service certificate and expected value ensures I am talking to the server I expect.
But now I consider the servers point of view. The server can have several RootCA certificates in its certificate store. If the client certificate send from the requester was issued (and signed) by one of these RootCA my service will trust this client certificate. How can I ensure that my service will only trust certificates issued by one determined RootCA? And how can I configure the one trusted RootCA?
(I think this could be some kind of an equivalent way of certificate pinning for client certificates issued by on RootCA)
The only way to achieve this would be to roll your own custom x509 certificate validator. Then for each incoming request you can simply check to make sure the client certificate was issued by your root CA. It's actually quite easy to do. You can find the steps to implement this here:
How to: Create a Service that Employs a Custom Certificate Validator

How to determine a server's list of CA certificates that it will accept from client?

According to https://wiki.jasig.org/display/CASUM/X.509+Certificates,
After the Server sends the certificate that identifies itself, it then can then send a list of names of Certificate Authorities from which it is willing to accept certificates.
I am wondering how to determine what this list is, and how to modify it.
The reason I am asking is that I am getting an infinite redirect between my server and my client after successful validation (i.e., the ticket stage), and I think it has to do with the CAS server not recognizing the CAS client's certificate (the client's certificate is self-signed).
If you want to see what this list is, you can use OpenSSL:
echo "" | openssl s_client -connect your.server:port
This will show various messages regarding the handshake, including the certificates and the list of CAs in the CertificateRequest message.
Ultimately, it's determined by the active X509TrustManager's getAcceptedIssuers() method. By default, this will be the list of Subject DNs of all your trust anchors (that is, the Subject DNs of all the certificates in your trust store).
Your client certificate will have to be verified by the server. This is normally done during the handshake by the trust manager, which (unless tweaked) will build a chain to a known CA (or at least known cert if it's the user cert itself) in the trust store.
Adding your self-signed certificate to your trust store should be sufficient. It doesn't have to be the cacerts file bundled with the JVM, you could make a copy of it and use the trust store settings of Apache Tomcat's connector to set it up.