SSL TrustStore without CA, only client certificate - ssl

I have a custom CAroot.crt (generated by me) and all my clients certificates are signed with this CAroot.crt. I have a TrustStore.jks where I put only clients certificates and not CAroot.crt, because I would like to have the possibility to remove a client from my truststore any time.
When I try to start my application I got the following:
*** Certificate chain
<Empty>
***
main, fatal error: 42: null cert chain
This is my trustStore.jks:
Keystore type: jks
Keystore provider: SUN
Your keystore contains 1 entry
Alias name: localhost
Creation date: Nov 23, 2019
Entry type: trustedCertEntry
Owner: CN=localhost, OU=IT, O=QUEROBUY, L=SAO CAETANO DO SUL, ST=SAO PAULO, C=BR
Issuer: CN=localhost, OU=IT, O=LANHELLAS XYZ, L=SAO CAETANO DO SUL, ST=SAO PAULO, C=BR
Serial number: 5416c04e360f9d50323c52d8a5b04be2969c9b86
Valid from: Sat Nov 23 16:39:54 BRT 2019 until: Tue Apr 06 16:39:54 BRT 2021
Certificate fingerprints:
MD5: 8F:29:1C:1F:05:89:0B:E6:A0:57:84:FE:B0:78:68:2D
SHA1: 95:C8:EA:0E:C8:7C:4E:99:E4:73:85:49:57:D6:BB:88:AF:52:52:12
SHA256: 7E:ED:19:AF:02:DB:CC:88:98:D0:10:4E:39:67:AA:4D:3F:70:DA:76:03:1B:CB:41:06:DC:3B:51:38:16:78:5F
Signature algorithm name: SHA256withRSA
Subject Public Key Algorithm: 2048-bit RSA key
Version: 1
*******************************************
*******************************************
If I add "CARoot.crt" to my trustStore.jks everything works fine, but I lose the chance to invalidate some specific client certificate. Imagine, I have 10 clients, each one with your own certificate (.crt) assigned by "CARoot.crt" but Client 001 for some reason should be invalidated immediately, I will just remove your public key from trustStore.jks in server, without that I should wait for certificate expiration date.

WHY: if you look slightly earlier in the debuglog you will see the CertificateRequest message, which specifies (among other things) the Cert[ificate] Authorities the server asks for; see rfc5246. Java defines this as the Subject names of the certs in the truststore, because the certs in the truststore are normally expected to be CA certs (and usually CA root certs, as the predefined ones are). Most client software obeys this instruction, though there are exceptions. Thus if your truststore contains certs like
Subject=Client1 Issuer=MyCA
Subject=Client2 Issuer=MyCA
Subject=Client3 Issuer=MyCA
then your server will ask for certs issued by any of Client1 Client2 Client3 but not MyCA. If the client actually has only one cert and it is for e.g. Client2 but is issued by MyCA -- not by any Clientn -- most client software will consider that cert unacceptable for this server/handshake.
THE PKI WAY. It is not true that a certificate can't be invalidated before expiration. PKI in general is explicitly designed to deal with such cases, which are generically termed revocation. There are various reasons a certificate can be revoked before expiring; for the particular PKI scheme used for SSL/TLS (and by Java for other things as well, like code signing), namely PKIX (or the effectively equivalent X.509) see rfc5280 5.3.1 as well as the rest of section 5 for Certificate Revocation Lists (CRLs), the older and traditional way of handling revocation, and rfc6960 for Online Certificate Status Protocol (OCSP), the newer way.
For the 'real' (public) PKI this mostly just works. Java implements PKIX revocation checking, but for SSL/TLS (JSSE) it is disabled by default; you must set sysprop com.sun.net.ssl.checkRevocation to use it. You may also need to set com.sun.security.enableCRLDP for CRLs and AFAICT always need to set security property (not sysprop) ocsp.enable for OCSP. See e.g.:
Check X509 certificate revocation status in Spring-Security before authenticating
Checking a X509 Certificate Revocation
Java SSL Certificate Revocation Checking
But running CRL distribution point(s) and/or OCSP responder(s) so that they are correct and available when needed -- which can be any time -- is non-trivial; this is one of the things real CAs charge money for (or get subsidized). Doing this for your own personal CA can vary from a pain in the butt to effectively impossible, but if you want to, be much more specific about your CA.
Your situation is in principle simpler; you have only one CA and you operate it, so you know when revocations occur. You could trivially provide the CRL(s) to the server when it(they) change. But AFAICS the builtin code has no way to use that information, so you'll have to use the hook that allows writing your own TrustManager in place of the builtin one, and modify it to use a validator with a CertStore that uses the CRLs. This is probably also a fair bit of work, but only once.
WORKAROUNDS. Instead of doing it 'right' in PKI terms, you could continue with your approach of trusting the leaf certs individually by modifying the server or the clients.
You could change the (X509)TrustManager hook in the server to validate certs as usual, but override getAcceptedIssuers to return a different (and correct for your case) list of 'requested' CAs, causing JSSE to send a CertificateRequest that causes the client(s) to use their correct cert(s).
Depending on the clients you might be able to change them to ignore the 'requested' CAs and send their cert anyway -- which the server's default TrustManager will validate if it is in the truststore (or the CA is). For OpenSSL this is easy; OpenSSL already ignores the requested CA list and just sends whatever is configured. For Java you could hook the client KeyManager and override chooseClientAliases method to not check against the list of desired issuers as it normally does. For other clients add to your Q or ask a new one.

TrustStore.jks is how java will determine whether or not it trusts a certificate. Its essentially a keystore file of root certificates. You will have to add your root certificate to this store if you want to avoid "not trustes messages."
In your scenario, since you used that root to issue 10 client certificates, removing the client_certificate.crt from your trust or key stores wont distrust it. Because, by design, java is looking at the TrustStore and finding the root certificate and thus trusting client_certificate.crt still. You are going to need a redesign of your CA.
When deploying any sort of PKI infrastructure, you will need to also set up CRL and/or OCSP servers. These are the two protocols used to revoke a certificate.
I also recommend you do not issue "end user certificates" directly from the root. Its best issue intermediate certificates off the root, and then use intermediate_certificate.crt to issue your actual server or client certs.
For a reason why, see here: https://security.stackexchange.com/questions/128779/why-is-it-more-secure-to-use-intermediate-ca-certificates#128800
And here is a great and easy way to deploy your own CA using OpenSSL. It will show you how to set up the root, intermediate, and revocation servers. https://jamielinux.com/docs/openssl-certificate-authority/

Related

Why does SSLLabs say my certificate is fine when my CA's cert is expired?

I did a scan of my domain using ssllabs.com, and this is what it says:
From what I can tell, one of my CAs is USERTrust RSA Certification Authority, and their certificate is expired, which SSLLabs flagged red, but it still says that there are no chain issues, and no browsers complain about talking to my domain.
I did the check after curl was complaining about an expired cert when talking to my domain, which may or may not be related.
What's going on here? How can an expired CA certificate not be a problem?
The expired certificate is actually not used for validation. It is unnecessarily send by your server, i.e. you could remove it from the certificate chain you send since modern system have a trusted CA builtin which effectively replaces this expired intermediate CA. For more details see for example USERTrust Intermediate Expiration in 2020. To cite:
This is an old intermediate certificate and modern operating systems have a new version available and won't be affected. ... Based on what we know, equipment released or receiving security updates after June 2010 will most likely not be affected. ...

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.

mTLS ( mutual TLS) details

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.

Renew SSL root CA cert - client programs in the wild need update?

Here is my situation:
I have a client application that I'm going to distribute - we'll call it MyClient.
MyClient does some SSL communication with one of our servers.
MyClient has the root CA embedded in it, so it can do proper verification of the server certificate.
Now, suppose some years go by, the root CA expires, and is renewed.
Does that mean I need to patch MyClient in the wild?
In other words, will a change to the validity dates on the certificate cause it to no longer match the baked-in root CA in MyClient?
Addendum: Suppose I write my client to not validate the date of the cert (but everything else). Then, when the root CA expires and is re-issued, do I still need to patch? Will other parts that play into the validation change, other than the date?
If your client is ensuring an SSL server certificate is issued by a particular root CA and that root CA is included in the client then yes, you will need to patch your client to replace the root CA certificate.
There are few good ways of doing this. What tends to happen is that root CA certificates are very long lived and use shorter lived intermediate CAs to issue SSL certificates but it sounds like this is not the case here.
Looking on the bright side, I do not know what algorithms were used with the old root CA certificate but, hopefully, The new root CA certificate will hopefully use a larger key (2048-bit RSA rather than 1024-bit or 512-bit) and a better hashing algorithm (SHA1 or better rather than MD5) so it may be a good opportunity to increase security.

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.