We're using HAProxy (v1.6.9) to verify client certificates(X.509 v3) and do SSL termination. We want to validate the Extended key usage and the basic constraints extensions in the client certificate. By looking at the HAProxy source code and OpenSSL verify documentation, I guess the keyUsage extension would be validated. But, does it do validation of the Extended Key Usage as well as Basic constraints extensions? If not, then is there a way to set this information in the request header and forward it to the application, so that the application can validate it?
Based on my understanding of OpenSSL documentation, we need to set the "purpose" option in verify to validate these extensions and I couldn't find this option being set in HAProxy's source code. Please correct me, if I'm wrong.
Thank you.
When you use commandline verify or call X509_verify_cert you do need to specify 'purpose' if you want it checked, because these operations can handle cert chains used for any purpose (or combination of purposes). For SSL/TLS protocols this is handled automatically, because the libssl code knows that it received and is checking a cert/chain for an 'SSL' (including TLS) server or client; see function ssl_validate_cert_chain in ssl/ssl_cert.c -- this line has the effect of specifying 'purpose'. For release 1.1.0 only (so far) the purpose is also checked against optional local trust attributes for the anchor/root (in addition to ExtendedKeyUsage extension(s)).
The statement on the man page that "If the -purpose option is not included then no checks [of untrusted cert extensions] are done." may be misleading. If purpose is not specified no checks for purpose are done, but some checks of extensions are always done regardless of purpose -- at least always for a proper chain with CA cert(s) distinct from the EE cert; for a selfsigned EE cert, which acts as its own anchor and effectively its own CA, some checks are inapplicable.
In particular, as mentioned a few paragraphs earlier, during the build phase CA (aka issuer) certs are checked for KeyUsage.certSign and SKI/issuer/serial matching AKI. During the second phase all untrusted certs are checked for BasicConstraints (and the now-rare NetscapeCertType) and KeyUsage.certSign (again), and NameConstraints (since 1.0.0), and the EE SAN or Subject is matched if a peer id was configured (which generally makes sense only if the peer is the server, hence not your case, and only since 1.0.2); also revocation checking (which may involve extensions) if configured (not the default); and if purpose checking is configured (which it is for libssl) in addition to ExtKeyUsage on all untrusted certs it checks KeyUsage and NetscapeCertType of the EE cert. Although the EE KeyUsage check here is not specific to the ciphersuite and thus keyexchange method negotiated; that is checked separately in libssl.
And the final phase in addition to checking signatures and validityperiod (as stated) also checks the RFC3779 extensions if used (almost never?) and if configured (probably rare) CertificatePolicies plus PolicyMappings, PolicyConstraints, and InhibitAnyPolicy.
Do note extension checks only fail if the extension is present with an inconsistent value; if an extension is omitted (by the CA) from a given cert it effectively permits everything related to that extension.
HTH.
Related
I have a server and a few clients, all running on different docker containers. The users can use the client by entering localhost:3000 on their browser (where the client docker is running).
All the containers run on the same LAN. I want to use HTTPS.
Can I sign a public private key pair using my own CA, then load the CA's public key to the browser?
I want to use the normal flow for public domains, but internally with my own CA.
Or should I look for another solution?
Meta: since you've now disclosed nodejs, that makes it at least borderline for topicality.
In general, the way PKIX (as used in SSL/TLS including HTTPS) works is that the server must have a privatekey and matching certificate; this is the same whether you use a public CA or your own (as you desire). The server should also have any intermediate or 'chain' cert(s) needed to verify its cert; a public CA will always need such chain cert(s) because CABforum rules (codifying common best practice) prohibits issuing 'subscriber' (EE) certs directly from a root, while your own CA is up to you -- you can choose to use intermediate(s) or not, although as I say it is considered best practice to use them and keep the root privatekey 'offline' -- in cryptography, that means not on any system that communicates with anybody, such as in this case servers that request certificates, thus eliminating one avenue of attack -- on a specialized device that is 'airgapped' (not connected or even able to be connected to any network) and in a locked vault, possibly with 'tamper protection', a polite name for self-destruct. As a known example of the rigor needed to secure something as sensitive as the root key of an important CA, compare Stuxnet.
The client(s) does not need and should not be configured with the server cert unless you want to do pinning; it(they) do need the CA root cert. Most clients, and particularly browsers, already have many/most/all public CA root certs builtin, so using a cert from such a CA does not require any action on the client(s); OTOH using your own CA requires adding your CA cert to the client(s). Chrome on Windows uses the Microsoft-supplied (Windows) store; you can add to this explicitly (using the GUI dialog, or the certutil program or powershell), although in domain-managed environments (e.g. businesses) it is also popular to 'push' a CA cert (or certs) using GPO. Firefox uses its own truststore, which you must add to explicitly.
In nodejs you configure the privatekey, server cert, and if needed chain cert(s), as documented
PS: note you generally should, and for Chrome (and new Edge, which is actually Chromium) must, have the SubjectAlternativeName (SAN) extension in the server cert specify its domain name(s), or optionally IP address(es), NOT (or not only) the CommonName (CN) attribute as you will find in many outdated and/or incompetent instructions and tutorials on the Web. OpenSSL commandline makes it easy to do CommonName but not quite so easy to do SAN; there are many Qs on several Stacks about this. Any public CA after about 2010 does SAN automatically.
We have an Microsoft Active Directory Domain with a large pool of domain controllers (DC) that are are setup with LDAP. These are all setup with LDAPS and uses Certificate Services via a template to setup a certificate with the domain name (i.e. test.corp) in the Subject Alternate Name (SAN) for the LDAPS server to serve.
Since these are DC's, DNS is setup in a pool for each these systems to respond to requests to test.corp in a round robin fashion.
Each of these DC's have multiple templates and multiple certificates in the Local Computer\Personal Certificate Store.
Upon testing, using a nodejs module, ldapjs when making a LDAPS request using the domain name, test.corp we notice that a handful of servers fail with the following message:
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match
certificate's altnames: Host: test.corp. is not in the cert's
altnames: othername:, DNS:.test.corp
As we investigated we found that these handful of LDAPS servers are serving the incorrect certificate. We determined this by using the following command
openssl s_client -connect .test.corp:636
If you take the certificate section of the output and put it in a file and use a tool such as the Certificate manager or certutil to read the file, you can see the certificate is not the correct one. (It does not have the domain "test.corp" SAN). We also verified this by comparing the Serial Numbers
As we investigated, since we have DC's that have multiple certificates in the Local Computer\Personal Certificate store, we came across the following article:
https://social.technet.microsoft.com/wiki/contents/articles/2980.ldap-over-ssl-ldaps-certificate.aspx
It suggests putting the certificate from the local computer\Personal certificate store to the Active Directory Domain Service\Personal store. We followed the steps outlined but we found the same results.
Upon further investigation, it was suggested to use a tool called ldp or adsiedit. We then proceeded to use these tools and spoofed the local machine's host file we were doing the test from, to point the domain (test.corp) to the ip's of one of the DC's that are giving us trouble. After a restart to clear any cache we tested the "ldp" and "adsiedit" tools to connect to test.corp. These systems did not report any errors.
We found this odd, we then ran the openssl command to see what certificate it was serving from this same system and we found it was still serving the incorrect certificate.
Upon further research, it appears that the "ldp" upon selecting the SSL checkbox and "adsiedit" tools were not compliant with RFC6125, specifically B.3
https://www.rfc-editor.org/rfc/rfc6125#appendix-B.3
, which basically states the identity of the certificate must match the identity of the request otherwise the handshake would fail. This identity verification is done by using the certificate common name (CN) or the SAN.
Based on this appears the tools "ldp" and "adsiedit" are not conforming to the RFC6125 standard.
All this to say, we need to first fix the handful of domain controllers that are serving the correct certificate. We are open to suggestions since we have been working on this problem for the past few months. Second, is there a way to get the MS tools in question to work to the RFC6125 standard?
This has been moved to:
https://serverfault.com/questions/939515/ldaps-microsoft-active-directory-multiple-certificates-rfc6125
RFC6125 specifically states that it does not supersede existing RFCs. LDAP cert handling is defined in RFC4513. Outside of that, RFC6125 has significant flaws. See also https://bugzilla.redhat.com/show_bug.cgi?id=1740070#c26
LDP will supposedly validate the SSL against the client store if you toggle the ssl checkbox on the connection screen.
That said, I'm not surprised that neither it nor ADSI edit enforce that part of the standard given they are often used to configure or repair broken configurations. Out of the box and without Certificate Services they use self signed certs on LDAPS. I would wager 80% of DCs never get a proper certificate for LDAP. If they enforced it most wouldn't be able to connect. A better design decision would have been to toggle off the validation.
I use a similar openssl command to verify my own systems. I think it's superior to LDP even if LDP were to validate the certificate. To save you some effort, I would suggest using this variant of the openssl command:
echo | openssl s_client -connect .test.corp:636 2>/dev/null | openssl x509 -noout -dates -issuer -subject -text
That should save you having to output to a file and having to read it with other tools.
I've found LDAPS on AD to be a huge pain for the exact reasons you describe. It just seems to pick up the first valid cert it can find. If you've already added it to the AD DS personal store, I'm not sure where else to suggest you go other than removing some of tother certs from the DCs computer store.
I have written a TLS code which is doing mutual authentication at Java, so client is sending its certificate after server sends its certificate. I would like to validate all the certificates in certificate chain by OCSP which is coming from client side to server side.
I have written my loop logic as assuming that last certificate is root(CA) certificate in the chain and not to send any OCSP query for it;
int certificateChainSize= x509Certificates.length;
// Verifies certificate chain respectively (issuer certificate required).
CertificateResult response = null;
try {
for (int i = 0; i < certificateChainSize-1 ; i++) {
response = client.verify(x509Certificates[i], x509Certificates[i+1]);
}
} catch (OcspException e) {
e.printStackTrace();
}
When I test TLS and get Wireshark capture, I realized that Google Chrome as client has been sending certificate chain without root. As a result; intermediate certificate is not queried because of loop logic, because my code assumed the intermedite certificate is root.
How can I force client to send all nodes of the certificate chain?
Thanks
I realized that Google Chrome as client has been sending certificate chain without root.
That is perfectly normal and the only correct behavior.
The root certificate is the trust anchor which has to be local to the party validating the certificate. Even if it is send it should be ignored when validating the certificate, i.e. only a local trust anchor should be used - otherwise a man in the middle could just provide his own certificate chain including his own root certificte. This means that in this case the server must have the root certificate already locally and thus there is no need for the client to send it.
In other words: don't try to change how Chrome behaves but instead adjust your expectations (and your code) on what the correct behavior is.
I agree with Steffen but to give some more facts, here is what TLS 1.3 explicitly says:
certificate_list: A sequence (chain) of CertificateEntry structures,
each containing a single certificate and set of extensions.
and
The sender's certificate MUST come in the first
CertificateEntry in the list. Each following certificate SHOULD
directly certify the one immediately preceding it. Because
certificate validation requires that trust anchors be distributed
independently, a certificate that specifies a trust anchor MAY be
omitted from the chain, provided that supported peers are known to
possess any omitted certificates.
and finally about ordering:
Note: Prior to TLS 1.3, "certificate_list" ordering required each
certificate to certify the one immediately preceding it; however,
some implementations allowed some flexibility. Servers sometimes
send both a current and deprecated intermediate for transitional
purposes, and others are simply configured incorrectly, but these
cases can nonetheless be validated properly. For maximum
compatibility, all implementations SHOULD be prepared to handle
potentially extraneous certificates and arbitrary orderings from any
TLS version, with the exception of the end-entity certificate which
MUST be first.
So Chrome is correctly applying this specification. You need to change your end to cope with it.
I know basic steps of a TLS Handshake but I don't have knowledge about detailed verification steps of certificates during TLS. My question is at below;
Let us assume that our system supports OCSP/CRLs verification. In this case, is the first step to verify incoming certificate using CRLs or OCSP Responder? Or look for the incoming certificate is in TrustStore or not, at first?
Thanks four your help
You will probably not find a definitive document on that, in part because people have shifted away from these 2 methods of revocation check (they are disabled by default in some browsers or when enabled do not produce very useful UI)
Nowadays people prefer OCSP Stapling (see https://casecurity.org/2013/02/14/certificate-revocation-and-ocsp-stapling/ for some reasons) over CRLs or OCSP.
But to go back precisely to your question, since both CRLs and OCSP typically mean going to fetch something remotely, this needs network access and can create delays. So if you have a local TrustStore with all the certificates you should first check that, before having to do a remote call.
Also the CABForum Baseline Requirements (at https://cabforum.org/baseline-requirements-certificate-contents/) specify that CRLs endpoints MAY be present in a certificate but OCSP endpoint MUST be present, except if you are doing OCSP Stapling instead.
On top of that EV certificates mandate OCSP.
So, in short it may make sense to do things in this order:
Check in local TrustStore
Do an OCSP query
Download a CRL
Note that nowadays you have also other options:
DANE, hence checking DNS TLSA records
Certificate Transparency Logs
oneCRL / CRLset
I need to setup https for multiple domians xxxx.com xxxx.net (with single common certificate)
CA where we buying certificate ask to create Certificate Signing Request (CSR), but when I'm generating it with openssl - it asks only for one name
how to make one CSR for multiple domains ?
Avoid certificates with multiple CNs (as suggested in comments), that's not how the specifications (RFC 2818 and RFC 6125) say it should work and, although it may work in some clients applications, it will usually fail. From RFC 2818:
If a subjectAltName extension of type dNSName is present, that MUST
be used as the identity. Otherwise, the (most specific) Common Name
field in the Subject field of the certificate MUST be used. Although
the use of the Common Name is existing practice, it is deprecated and
Certification Authorities are encouraged to use the dNSName instead.
Instead, generate certificates (or CSR) with multiple Subject Alternative Names (SANs).
If you're using OpenSSL, edit your openssl.cnf (or edit a copy) and set these properties, in the relevant sections ([req] and [ v3_req ]):
[req]
req_extensions = v3_req
[v3_req]
subjectAltName=DNS:www.example1.com,DNS:www.example2.com,DNS:www.example3.com
There's also a nice trick to use an environment variable for this (rather in than fixing it in a configuration file) here: http://www.crsr.net/Notes/SSL.html
You may also want to have one of them (any) in the CN.
(You may also be interested in this answer.)